web-dev-qa-db-fra.com

L'expression fait référence à une méthode qui n'appartient pas à l'objet simulé

J'ai un service api qui appelle un autre service api. Lorsque j'ai configuré les objets Mock, cela a échoué avec une erreur:

NotSupportedException: expression fait référence à une méthode qui n'appartient pas à l'objet simulé.

Voici le 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() });
}

J'ai lu Test d'expression avec Moq mais cela n'a pas fonctionné pour mon cas. Si je supprime cette _mockCarrierService.Setup(), le scénario de test peut s'exécuter mais échoue avec un NullReferenceException car il n'avait pas de configuration List<IQueryable<AccountSearchModel>> Valide.

Avez-vous une idée de comment je peux y parvenir?


Note de bas de page: solution actuelle

FWIW, voici la solution que j'utilise actuellement. Je suis à l'écoute d'une meilleure approche du problème (jusqu'à ce que Moq commence à prendre en charge les méthodes d'extension moqueuses).

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);
}

Note de bas de page: cadre de simulation alternatif

J'ai également trouvé un framework de moquerie commercial qui prend en charge la méthode d'extension de moquerie et un lien vers les documents pratiques: Telerik JustMock .

38
display name

Ce problème se produit car vous essayez de simuler la méthode Select, qui est une méthode d'extension , pas une méthode d'instance de IEnumerable<T>.

Fondamentalement, il n'y a aucun moyen de se moquer d'une méthode d'extension. Jetez un oeil à cette question pour quelques idées que vous pourriez trouver utiles.

UPD (12/11/2014):

Pour mieux comprendre les méthodes d'extension moqueuses, pensez aux points suivants:

  • Bien que les méthodes d'extension soient appelées comme s'il s'agissait de méthodes d'instance sur le type étendu, elles ne sont en fait que des méthodes statiques avec un peu de sucre syntaxique.

  • Les méthodes d'extension de l'espace de noms System.Linq Sont implémentées sous la forme fonctions pures - elles sont déterministes et n'ont aucune observation effets secondaires . Je suis d'accord que les méthodes statiques sont mauvaises, sauf celles qui sont des fonctions pures - j'espère que vous serez également d'accord avec cette affirmation :)

  • Donc, étant donné un objet de type T, comment implémenteriez-vous la fonction statique pure f(T obj)? Cela n'est possible qu'en combinant d'autres fonctions pures définies pour l'objet T (ou toute autre fonction pure, en fait), ou en lisant un état global immuable et déterministe (pour garder la fonction f déterministe et sans effet secondaire). En fait, "état global immuable et déterministe" a un nom plus commode - une constante.

Ainsi, il s'avère que si vous suivez la règle selon laquelle les méthodes statiques doivent être des fonctions pures (et il semble que Microsoft suit cette règle, au moins pour les méthodes LINQ), se moquant d'une méthode d'extension f(this T obj) devrait être réductible à la simulation des méthodes non statiques ou de l'état utilisé par cette méthode d'extension - simplement parce que cette méthode d'extension repose sur les méthodes et l'état de l'instance obj dans sa mise en œuvre (et éventuellement sur les autres fonctions pures et/ou valeurs constantes).

Dans le cas de IEnumerable<T>, La méthode d'extension Select() est implémentée en termes d'instruction foreach qui, à son tour, utilise GetEnumerator() méthode. Vous pouvez donc vous moquer de GetEnumerator() et obtenir le comportement requis pour les méthodes d'extension qui en dépendent.

66
Sergey Kolodiy

Vous avez:

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

Vous vous moquez donc de IEnumerable<>. Le seul membre IEnumerable<> Possède une méthode GetEnumerator() (plus une autre méthode avec la même signature GetEnumerator() héritée de l'interface de base). La méthode Select est vraiment une méthode d'extension (comme cela a été souligné dans la première réponse) qui est une méthode statique qui fonctionne en appelant GetEnumerator() (éventuellement via l'instruction C # foreach ).

Il est possible de faire fonctionner les choses en faisant Setup de GetEnumerator sur votre maquette.

Cependant, il est beaucoup plus simple d'utiliser simplement un type concret et non simulé qui "est" IEnumerable<>, Tel que List<>. Alors essayez:

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

Ajoutez ensuite une entrée au List<>. Ce que vous devez ajouter, c'est un Mock<ICarrierApiService<AccountSearchModel>> Sur lequel GetFromApiWithQuery méthode est configurée.

7
Jeppe Stig Nielsen

aussi si vous devez vous moquer de la configuration IC, vous pouvez utiliser ce code ci-dessous:

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