Подтвердить что ты не робот

Выражение ссылается на метод, который не принадлежит к издеваемому объекту

У меня есть служба api, которая вызывает другую службу api. Когда я настраивал объекты Mock, сбой произошел с ошибкой:

NotSupportedException: выражение ссылается на метод, который не принадлежит к издеваемому объекту.

Это код:

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

Я читал Экспрессирование с помощью Moq, но это не помогло мне. Если я удалю этот _mockCarrierService.Setup(), тестовый сценарий может работать, но с ошибкой NullReferenceException, потому что у него не было установленного значения List<IQueryable<AccountSearchModel>>.

Любая идея, как я могу это достичь?


Сноска: текущее решение

FWIW, вот решение, которое я сейчас использую. Я все уши для лучшего подхода к проблеме (пока Moq не начнет поддерживать насмешливые методы расширения).

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

Сноска: альтернативная издевательская структура

Я также нашел коммерческую насмешливую структуру, которая поддерживает насмешливый метод расширения и ссылку на практические документы: Telerik JustMock.

4b9b3361

Ответ 1

Эта проблема возникает из-за того, что вы пытаетесь смоделировать метод Select, который является методом расширения, а не методом экземпляра IEnumerable<T>.

По сути, нет способа высмеять метод расширения. Посмотрите этот вопрос, чтобы найти некоторые идеи, которые могут оказаться полезными.

UPD (12/11/2014):

Чтобы лучше понять методы насмешливых расширений, подумайте о следующем:

  • Хотя методы расширения вызываются так, как если бы они были методами экземпляра в расширенном типе, на самом деле они представляют собой просто статические методы с небольшим количеством синтаксического сахара.

  • Методы расширения из пространства имен System.Linq реализованы как чистые функции - они являются детерминированными и не имеют видимых побочных эффектов. Я согласен с тем, что статические методы являются злыми, кроме тех, которые являются чистыми функциями - надеюсь, вы согласитесь и с этим утверждением :)

  • Итак, учитывая объект типа T, как бы вы реализовали статическую чистую функцию f(T obj)? Это возможно только путем объединения других чистых функций, определенных для объекта T (или фактически любых других чистых функций), или путем считывания неизменяемого и детерминированного глобального состояния (чтобы функция f была детерминированной и не имела побочных эффектов), На самом деле, "неизменное и детерминированное глобальное состояние" имеет более удобное название - константа.

Итак, оказывается, что если вы следуете правилу, что статические методы должны быть чистыми функциями (и похоже, что Microsoft следует этому правилу, по крайней мере, для методов LINQ), то насмешка над методом расширения f(this T obj) должна быть сведена к насмешке без статические методы или состояние, используемые этим методом расширения - просто потому, что этот метод расширения опирается на методы экземпляра obj и состояние в своей реализации (и, возможно, на другие чистые функции и/или постоянные значения).

В случае IEnumerable<T>, метод расширения Select() реализован в терминах оператора foreach, который, в свою очередь, использует метод GetEnumerator(). Таким образом, вы можете смоделировать GetEnumerator() и добиться необходимого поведения для методов расширения, которые полагаются на него.

Ответ 2

У вас есть:

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

Итак, вы издеваетесь IEnumerable<>. Единственный член IEnumerable<> имеет метод GetEnumerator() (плюс другой метод с той же сигнатурой GetEnumerator(), унаследованной от базового интерфейса). Метод Select действительно является методом расширения (как было указано в первом ответе), который является статическим методом, который работает, вызывая GetEnumerator() (возможно, через оператор С# foreach).

Можно сделать все, выполнив Setup GetEnumerator в вашем макете.

Однако гораздо проще просто использовать конкретный, не-макетный тип, который "есть" IEnumerable<>, например List<>. Поэтому попробуйте:

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

Затем добавьте запись в List<>. То, что вы должны добавить, это Mock<ICarrierApiService<AccountSearchModel>>, на котором установлен GetFromApiWithQuery Method.

Ответ 3

Кроме того, если вам нужно смоделировать IConfiguration, вы можете использовать следующий код:

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