Как написать 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 ответа:
Хороший способ разрешить вам писать заглушки-это использоватьинъекцию зависимостей .
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")); } }