Макет (Moq) с контейнером Unity и дженериками

#c# #unit-testing #generics #moq #unity-container

#c# #модульное тестирование #дженерики #moq #unity-контейнер

Вопрос:

У меня есть интерфейс контейнера с универсальным методом разрешения, который при реализации разрешается с использованием Unity.

 public interface IContainer
{
    T Resolve<T>();
}
  

В настоящее время существует макет класса контейнера:

 public class MockContainer : IContainer {
    public T Resolve<T>()
    {            
        return default(T);
    }
}
  

Но я хочу использовать Moq для согласованности и сделать эквивалентный:

 mockContainer
    .Setup(container => container.Resolve<T>())
    .Returns(default(T));
  

Но я получаю 2 ошибки компиляции 'T' could not be found

Является ли синтаксис Moq неправильным, я пропускаю ссылку или это просто невозможно сделать?

Редактировать Я знаю, что издевательство над контейнером не идеально. Я мог бы разрешить каждый класс один за другим в своем коде настройки.

Мой вопрос заключается в том, существует ли какой-либо синтаксис Moq для выполнения эквивалента моего фактического контейнера.

Комментарии:

1. Я думаю, справедливый вопрос: зачем вам все равно нужно издеваться над разрешением, разве вы не используете внедрение зависимостей?

Ответ №1:

Обычно вы не можете этого сделать (настроить общие методы без указания типа), но если вы хотите только return default(T) и вам все равно, вызывается ли функция вообще, достаточно просто создать макет без вызова Setup :

 var mockZincContainer = new Mock<IContainer>();

// works just fine
mockZincContainer.Object.Resolve<DateTime>(); // returns default(DateTime)
mockZincContainer.Object.Resolve<object>(); // returns default(object)
  

Вы даже можете возвращать макеты вместо default(T) этого, изменяя DefaultValue свойство макета с DefaultValue.Empty на DefaultValue.Mock .

Ответ №2:

Вам не нужно извлекать макет объекта из вашего интерфейса. Так что избавьтесь от MockContainer.

На этом этапе в вашем модульном тестировании вы должны знать, какие типы вы хотите протестировать. Итак, на этом этапе просто дайте ему реальный тип и скажите, что возвращать.

т.е.

 //arrange
mockZincContainer
    .Setup(container => container.Resolve<ISomeInterface>())
    .Returns(new ConcreteClassThatDerivesFromSomeInterface());
var testClass = new ClassToBeTested(mockZincContainer.Object);

//act
testClass.DoYourMethod();

//assert
mockZincContainer.Verify(c => c.Resolve<ISomeInterface>(), Times.Once);
  

Комментарии:

1. к сожалению, эти модульные тесты являются интеграционными тестами, и запись этих настроек и возвратов для каждой используемой зависимости требует времени и подвержена ошибкам.

Ответ №3:

Зачем вам издеваться над контейнером? Это скорее проблема интеграции. Весь ваш код должен быть тестируемым без подключения к контейнеру. Издевательство над контейнером для возврата зависимости, которую вы затем передаете потребляющим классам, кажется мне большой тратой времени.

Комментарии:

1. В идеале да. На самом деле модульные тесты — это интеграционные тесты, и у меня нет свободы переписывать ~ 50 000 модульных тестов