webentwicklung-frage-antwort-db.com.de

Ausdruck verweist auf eine Methode, die nicht zum verspotteten Objekt gehört

Ich habe einen API-Dienst, der einen anderen API-Dienst aufruft. Beim Einrichten der Mock-Objekte ist ein Fehler aufgetreten:

NotSupportedException: expression verweist auf eine Methode, die nicht zum verspotteten Objekt gehört.

Dies ist der Code:

private Mock<IEnumerable<ICarrierApiService<AccountSearchModel>>> _mockCarrierService;
private Mock<IApiService<AccountSearchModel>> _mockApiService;

[SetUp]
public void SetUp()
{
  _mockApiService = new Mock<IApiService<AccountSearchModel>>();
  _mockCarrierService = new Mock<IEnumerable<ICarrierApiService<AccountSearchModel>>>();
  _mockApiService.Setup(x => x.GetFromApiWithQuery(It.IsAny<string>())).Returns(ApiValue());

  // Error occurred when call _mockApiService.GetFromApiWithQuery() in .Select()
  _mockCarrierService.Setup(x => x
            .Select(s => s
                .GetFromApiWithQuery(It.IsAny<string>())).ToList())
                .Returns(new List<IQueryable<AccountSearchModel>> { ApiValue() });
}

Ich habe Ausdruckstest mit Moq gelesen, aber es hat in meinem Fall nicht funktioniert. Wenn ich diese _mockCarrierService.Setup() entferne, kann der Testfall ausgeführt werden, schlägt jedoch mit einem NullReferenceException fehl, da kein gültiger List<IQueryable<AccountSearchModel>> Eingerichtet wurde.

Irgendeine Idee, wie ich das erreichen kann?


Fußnote: Aktuelle Lösung

FWIW, hier ist die Lösung, die ich derzeit benutze. Ich bin gespannt auf eine bessere Herangehensweise (bis Moq anfängt, spöttische Erweiterungsmethoden zu unterstützen).

private List<ICarrierApiService<AccountSearchModel>> _mockCarrierService;
private AccountSearchController _mockController;
private Mock<ICarrierApiService<AccountSearchModel>> _mockApiService;

[SetUp]
public void SetUp()
{
   _mockApiService = new Mock<ICarrierApiService<AccountSearchModel>>();
   _carrierServiceMocks = new List<ICarrierApiService<AccountSearchModel>> { _mockApiService.Object };
   _mockApiService.Setup(x => x.GetFromApiWithQuery(It.IsAny<string>())).Returns(ApiValue());
   _mockController = new AccountSearchController(_carrierServiceMocks);
}

Fußnote: Alternativer Spottrahmen

Ich habe auch ein kommerzielles Verspottungs-Framework gefunden, das die Verspottungs-Erweiterungsmethode und den Link zu den Anleitungen unterstützt: Telerik JustMock .

38
display name

Dieses Problem tritt auf, weil Sie versuchen, die Select -Methode zu verspotten, bei der es sich um eine Erweiterungsmethode handelt, nicht um eine Instanzmethode von IEnumerable<T>.

Grundsätzlich gibt es keine Möglichkeit, eine Erweiterungsmethode zu verspotten. Schauen Sie sich diese Frage für einige Ideen an, die Sie vielleicht nützlich finden.

UPD (11.12.2014):

Um mehr über das Verspotten von Erweiterungsmethoden zu erfahren, sollten Sie Folgendes beachten:

  • Obwohl Erweiterungsmethoden so aufgerufen werden, als wären sie Instanzmethoden des erweiterten Typs, sind sie eigentlich nur statische Methoden mit ein wenig syntaktischem Zucker.

  • Erweiterungsmethoden aus dem System.Linq - Namespace werden als reine Funktionen implementiert. Sie sind deterministisch und haben keine beobachtbaren Nebenwirkungen . Ich bin damit einverstanden, dass statische Methoden sind böse, außer denen, die reine Funktionen sind - ich hoffe, dass auch Sie dieser Aussage zustimmen würden :)

  • Wie würden Sie also bei einem Objekt vom Typ T die statische reine Funktion f(T obj) implementieren? Es ist nur möglich, andere reine Funktionen zu kombinieren, die für das Objekt T definiert sind (oder tatsächlich alle anderen reinen Funktionen), oder den unveränderlichen und deterministischen globalen Zustand zu lesen (um die Funktion f deterministisch zu halten und nebenwirkungsfrei). Tatsächlich hat "unveränderlicher und deterministischer globaler Zustand" einen günstigeren Namen - eine Konstante.

Wenn Sie also die Regel befolgen, dass statische Methoden reine Funktionen sein sollen (und Microsoft scheint zumindest für die LINQ-Methoden dieser Regel zu folgen), verspotten Sie eine Erweiterungsmethode f(this T obj) sollte sich darauf reduzieren lassen, nicht-statische Methoden oder den von dieser Erweiterungsmethode verwendeten Status zu verspotten - einfach, weil diese Erweiterungsmethode auf den obj -Instanzmethoden und -Status in beruht seine Implementierung (und möglicherweise auf den anderen reinen Funktionen und/oder konstanten Werten).

Im Fall von IEnumerable<T> Ist die Erweiterungsmethode Select()implementiert in Bezug auf die Anweisung foreach, die wiederum die Methode GetEnumerator() verwendet . So können Sie GetEnumerator() verspotten und das erforderliche Verhalten für Erweiterungsmethoden erzielen, die darauf angewiesen sind.

66
Sergey Kolodiy

Du hast:

_mockCarrierService = new Mock<IEnumerable<ICarrierApiService<AccountSearchModel>>>();

Sie verspotten also IEnumerable<>. Das einzige Mitglied, das IEnumerable<> Hat, ist eine Methode GetEnumerator() (plus eine andere Methode mit derselben Signatur GetEnumerator(), die von der Basisschnittstelle geerbt wurde). Die Select -Methode ist wirklich eine Erweiterungsmethode (wie bereits in der ersten Antwort erwähnt), bei der es sich um eine statische Methode handelt, die durch Aufrufen von GetEnumerator() (möglicherweise über die Anweisung C # foreach) funktioniert ).

Es ist möglich, Dinge zum Laufen zu bringen, indem Sie Setup von GetEnumerator auf Ihrem Mock ausführen.

Es ist jedoch viel einfacher, einfach einen konkreten, nicht nachgebildeten Typ zu verwenden, der IEnumerable<> Lautet, z. B. List<>. Also versuche:

_mockCarrierService = new List<ICarrierApiService<AccountSearchModel>>();

Fügen Sie dann einen Eintrag zu List<> Hinzu. Was Sie hinzufügen sollten, ist ein Mock<ICarrierApiService<AccountSearchModel>>, Auf dem die GetFromApiWithQuery -Methode eingerichtet ist.

7

auch wenn Sie IConfiguration verspotten müssen, können Sie diesen Code unten verwenden:

var builder = new ConfigurationBuilder()
        .AddInMemoryCollection(new Dictionary<string, string>
        {
            { "your-key", "your value" }
        });
        var config = builder.Build();
0