Есть ли веская причина использовать DI с модулем / классом без зависимостей в модульном тесте?


Инъекция зависимостей может оказаться очень полезной для тестирования модулей с зависимостями. Дело не в них.


Скажем, что есть какая-то конкретная реализация,

public class DoesSomething : IDoesSomething 
{
    public int DoesImportant(int x, int y) 
    { 
        // perform some operation 
    }
}

, который реализует это,

public interface IDoesSomething
{
    int DoesImportant(int x, int y); 
}

В модульном тесте вы, очевидно, можете new поднять тест,

[TestMethod]
public void DoesSomething_CanDoDoesImportant()
{ 
    int expected = 42; 

    IDoesSomething foo = new DoesSomething(); 
    int actual = foo.DoesImportant(21, 2); 

    Assert.AreEqual(expected, actual); 
}

Или использовать DI (Autofac здесь, но не должно иметь значения для принципа вопроса),

[TestMethod]
public void DoesSomething_CanDoDoesImportant()
{
    var builder = new ContainerBuilder();
    builder.RegisterType<DoesSomething>().As<IDoesSomething>();
    var container = builder.Build();

    int expected = 42;
    IDoesSomething foo = container.Resolve<IDoesSomething>();
    int actual = foo.DoesImportant(21, 2);

    Assert.AreEqual(expected, actual);
}

Учитывая такой автономный модуль без зависимостей, есть ли веская причина для введения IDoesSomething в тест? Или есть ли убедительная причина не вводить IDoesSomething?

2 3

2 ответа:

Нет необходимости использовать контейнер DI для этого теста.

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

Пример единства:

[TestMethod]
public void DoesSomething_behaves_correctly()
{
     var expected = 42;
     var container = new UnityContainer();
     var foo = container.Resolve<DoesSomething>(); 
     int actual = foo.DoesImportant(21, 21); 

     Assert.AreEqual(expected, actual); 
}
Побочное преимущество этого подхода состоит в том, что ваш тест нуждается в минимальных изменениях, когда DoesSomething начинают иметь зависимости.

Ваши тесты должны быть написаны специально для конкретной реализации .

Возьмем для примера:

public void DoTestA()
{
    ObjectFactory.Set<IDoesSomething, DoesSomethingBadly>();

    var doesSomething = ObjectFactory.Get<IDoesSomething>();
    Assert.AreEqual(0, doesSomething.Add(1,1));
}

public void DoTestB()
{
    int expected = 42; 

    //This test is now *completely* dependent on DoTestA, and can give different results
    //depending on which test is run first. Further, we don't know
    //which implementation we're testing here. It's not immediately clear, even if
    //there's only one implementation.
    //As its a test, it should be very explicit in what it's testing.

    IDoesSomething foo = ObjectFactory.Get<IDoesSomething>(); 
    int actual = foo.DoesImportant(21, 21); 

    Assert.AreEqual(expected, actual); 
}

// Define other methods and classes here
public class DoesSomething : IDoesSomething 
{
    public int Add(int x, int y) 
    { 
        return x+y;
    }
}

public class DoesSomethingBadly : IDoesSomething
{
    public int Add(int x, int y)
    {
        return x-y;
    }
}

public interface IDoesSomething
{
    int Add(int x, int y); 
}

В тестах прямая ссылка на класс-это, безусловно, правильный путь. Нас не волнует, что это интерфейс, нас волнует только конкретная реализация.

var foo = new DoesSomething(); это определенно лучший вариант.

IDoesSomething foo = new DoesSomething(); это не вредно, но кажется совершенно ненужным, так как, опять же, мы заботимся только о реализации, а не об интерфейсе.