C# тестирование - фиктивный класс в вызове конструктора - PullRequest
1 голос
/ 15 февраля 2020

Я новичок в использовании Moq с Xunit в Visual Studio 2019.

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

public class MockedClass : IMockedClass
{
   public MockedClass()
   {
      // don't call this
   }
   public List<string> Function1()
   { /* return a List<string> here */ }
}
public class MyClass
{
   readonly IMockedClass mockedClass = null;
   public MyClass()
   {
      mockedClass = new MockedClass();
   }
   public string Function2()
   { /* return a string here */ }
}
public class MyClassTest
{
   [Fact]
   public string Function2Test()
   {
      var returnMock = new List<string>() { "1", "2", "3" };
      var mockMockedClass = new Mock<IMockedClass>();
      mockMockedClass.Setup(x => x.Function1()).Returns(returnMock);
      var myClass = new MyClass();
      var result = myClass.Function2();
      ...
   }
}

Моя проблема в том, что в тесте Function2Test вызывается конструктор MyClass, который вызывает конструктор MockedClass , Но я не хочу, чтобы конструктор MockedClass вызывался.

Как я могу издеваться над конструктором MockedClass?

Ответы [ 2 ]

3 голосов
/ 15 февраля 2020

Я думаю, вам может понадобиться немного изменить свой код. Видите ли, получение макета интерфейса само по себе не меняет автоматически поведение других реализаций при создании экземпляров из других частей кода. Как правило, вы хотите внедрить свои смоделированные реализации в проверенный код с помощью Dependency Injection .

public class MyClass
{
    readonly IMockedClass mockedClass = null;
    public MyClass(IMockedClass c)
    {
        mockedClass = c; // now you'll get the correct implemetation
    }
    public string Function2()
    { /* return a string here */ }
}
void Main()
{

        var returnMock = new List<string>() { "1", "2", "3" };
        var mockMockedClass = new Mock<IMockedClass>(); // get your mock here
        mockMockedClass.Setup(x => x.Function1()).Returns(returnMock);
        var myClass = new MyClass(mockMockedClass.Object); // inject it here
        var result = myClass.Function2();
}

Затем вам нужно будет настроить контейнер Dependency Injection, чтобы предоставить вам конкретный MockedClass когда ваш фактический код приложения работает. В зависимости от вашего кода и требований, есть куча опций , поэтому я не буду рекомендовать конкретную c структуру здесь.

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

1 голос
/ 15 февраля 2020

Вы были в правильном направлении, чтобы высмеивать зависимости, но вам не хватает реализации принципа инверсии зависимости.

С вашим кодом как есть, вы привязываете MyClass к MockedClass, и, кроме того, вы позволяете MyClass контролировать время жизни объекта MockedClass.

Что вам нужно сделать, это изменить конструктор MyClass принять объект IMockedClass. Обратите внимание, что вы должны передать интерфейс, а не тип класса.

public class MyClass
{
   readonly IMockedClass mockedClass = null;
   public MyClass(IMockedClass mockedClass)
   {
      this.mockedClass = mockedClass();
   }
   public string Function2()
   { /* return a string here */ }
}

, и ваш тестовый код будет:

public string Function2Test()
   {
      var returnMock = new List<string>() { "1", "2", "3" };
      var mockMockedClass = new Mock<IMockedClass>();
      mockMockedClass.Setup(x => x.Function1()).Returns(returnMock);
      var myClass = new MyClass(mockMockedClass.Object);
      var result = myClass.Function2();
      ...
   }
...