Как написать stub метод с NUnit в C#


У меня есть 2 класса:

  • Первый шаг.cs
  • Вторая глубина.cs

    Я сделал простой код, например:


class FirstDeep
    {
        public FirstDeep() { }

        public string AddA(string str)
        {
            SecondDeep sd = new SecondDeep();
            bool flag = sd.SomethingToDo(str);

            if (flag == true)
                str = string.Concat(str, "AAA");
            else
                str = string.Concat(str, "BBB");

            return str;
        }
    }

И

class SecondDeep
    {
        public bool SomethingToDo(string str)
        {
            bool flag = false;
            if (str.Length < 10)
            {
                //todo something in DB, and after that flag should be TRUE
            }
            return flag;
        }
    }

Тогда я хочу написать модульный тест для метода "AddA":

class Tests
    {
        [Test]
        public void AddATest()
        {
            string expected = "ABCAAA";

            FirstDeep fd = new FirstDeep();
            string res = fd.AddA("ABC");

            Assert.AreEqual(expected, res);
        }
    }

И после этого у меня возникли проблемы, я не знаю, как правильно написать заглушку для метода SomethingToDo в моем тестовом классе. У меня всегда есть ложь. Я должен просто вернуть истину. Но как это сделать?

2 8

2 ответа:

Хороший способ разрешить вам писать заглушки-это использоватьинъекцию зависимостей . FirstDeep зависит от SecondDeep и в вашем тесте вы хотите заменить SecondDeep заглушкой.

Сначала измените существующий код, извлекая интерфейс для SecondDeep , а затем введите его в FirstDeep в конструкторе:

interface ISecondDeep {

  Boolean SomethingToDo(String str);

}

class SecondDeep : ISecondDeep { ... }

class FirstDeep {

  readonly ISecondDeep secondDeep;

  public FirstDeep(ISecondDeep secondDeep) {
    this.secondDeep = secondDeep;
  }

  public String AddA(String str) {   
    var flag = this.secondDeep.SomethingToDo(str);
    ...
  }

}
Обратите внимание, что FirstDeep больше не создает экземпляр SecondDeep. Вместо этого в конструктор вводится экземпляр.

В своем тесте вы можете создать заглушку для ISecondDeep, где SomethingToDo всегда возвращает true:

class SecondDeepStub : ISecondDeep {

  public Boolean SomethingToDo(String str) {
    return true;
  }

}

В тесте используется заглушка:

var firstDeep = new FirstDeep(new SecondDeepStub());

В производственном коде вы используете "реальный" SecondDeep:

var firstDeep = new FirstDeep(new SecondDeep());

Использование контейнера инъекции зависимостей и стаббинг-фреймворка может значительно упростить эту задачу.

Если вы не хотите переписывать свой код, вы можете использовать фреймворк для перехвата вызовов, например Microsoft Moles. В следующей версии Visual Studio аналогичная технология будет доступна в фейках Фреймворк .

Чтобы сделать ваш код тестируемым, не создавайте экземпляры зависимостей внутри класса. Используйте инъекцию зависимостей (через конструктор, свойство или параметр). Также используйте абстрактные классы или интерфейсы, чтобы позволить издеваться над зависимостями:

class FirstDeep
{
    private ISecondDeep oa;

    public FirstDeep(ISecondDeep oa) 
    { 
        this.oa = oa;
    }

    public string AddA(string str)
    {
       return String.Concat(str, oa.SomethingToDo(str) ? "AAA" : "BBB");
    }
}

В зависимости от абстракций позволяет тестировать класс изолированно.

interface ISecondDeep
{
   bool SomethingToDo(string str);
}

class SecondDeep : ISecondDeep
{
    public bool SomethingToDo(string str)
    {
       bool flag = false;
       if (str.Length < 10)
       {
           // without abstraction your test will require database
       }
       return flag;
    }
}

Здесь Образец теста (используя Moq ). Это показывает вам, как вы можете вернуться true от вызова к вашей издевательской зависимости:

[TestFixture]
class Tests
{
    [Test]
    public void AddAAATest()
    {
        // Arrange
        Mock<ISecondDeep> secondDeep = new Mock<ISecondDeep>();
        secondDeep.Setup(x => x.SomethingToDo(It.IsAny<string>())).Returns(true);
        // Act
        FirstDeep fd = new FirstDeep(secondDeep.Object);
        // Assert
        Assert.That(fd.AddA("ABD"), Is.EqualTo("ABCAAA"));
     }
}