Как написать метод заглушки с NUnit в C#

у меня 2 класса:

  • FirstDeep.cs
  • SecondDeep.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 ответов


хороший способ позволить вам писать заглушки-использовать инъекции зависимостей. 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 экземпляра. Вместо этого экземпляр вводится в конструктор.

In ваш тест вы можете создать заглушку для 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());

использование контейнера инъекций зависимостей и структуры stubbing может сделать многое из этого проще.

если вы не хотите переписывать свой код, вы можете использовать фреймворк для перехвата вызовов, таких как 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"));
     }
}