Вы совершенно правы, эти фрагменты не как вы обычно используете рамки изоляции. Это примеры использования, которые показывают, как использовать библиотеку nSubsitute. В вашем примере тест пройдет, потому что он использует динамически сгенерированный объект, который придерживается интерфейса IMathService, но запрограммирован на возврат 4, когда его метод Add вызывается с аргументами 2 и 3. Реальная реализация не используется нигде в этом контрольная работа.
nSubstitute и его альтернативы могут использоваться для тестирования взаимодействия класса и его сотрудников. Вы используете эти изоляционные рамки для тестирования классов по отдельности. Как правило, вы создаете конкретный экземпляр System Under Test (SUT), но предоставляете поддельные реализации для своих сотрудников. Затем вы можете установить ожидания для поддельных соавторов или утверждать, что на них были вызваны определенные методы, чтобы убедиться, что SUT правильно взаимодействует со своими сотрудниками. Вместо того, чтобы вручную раскручивать собственные поддельные коллабораторы, вы можете использовать среду изоляции для автоматизации этой повторяющейся работы для вас.
Пример, надеюсь, делает вещи более ясными. Предположим, я работаю над классом Foo, и я использую IBAR-совместителя, которого я еще не реализовал. IBar будет говорить с реальной базой данных, так что я не хочу использовать реальное осуществление IBar в моих изолированных модульных тестов для Foo:
public class Foo
{
private readonly IBar _bar;
public Foo(IBar bar)
{
_bar = bar;
}
public void DoSomething()
{
_bar.DoSomethingElse();
}
}
public interface IBar
{
void DoSomethingElse();
}
public class AnImplementationOfBar : IBar
{
public void DoSomethingElse()
{
throw new System.NotImplementedException();
}
}
[Test]
public void Foo_interacts_correctly_with_its_bar()
{
var fakeBar = Substitute.For<IBar>();
var foo = new Foo(fakeBar);
foo.DoSomething();
fakeBar.Received().DoSomethingElse(); //This is the assertion of the test and verifies whether fakeBar.DoSomethingElse has been called.
}
public static void Main(string[] args)
{
//Production code would look like this:
var foo = new Foo(new AnImplementationOfBar());
//next line throws because AnImplementationOfBar.DoSomethingElse has not been implemented yet
foo.DoSomething();
}
Замены могут быть использованы в примерно два сценария:
- Чтобы проверить, произошло ли правильное взаимодействие (например, пример кода из моего ответа выше, используя метод Received()). Как правило, слово mock используется для подделок с этой целью.
- Чтобы предоставить фиктивные данные SUT, чтобы производственный код выполнял определенный путь кода. Обычно для такого рода подделок используется слово stub. Тест IMathService является типичным примером того, как вы должны настроить заглушку с помощью метода Returns().
Если вы знакомы с определением Устройте-Act-Assert для модульных тестов: Столбики являются часть Устройте фазу теста, так как они создали фиктивные данные, которые требуют вашего SUT. Mocks являются частью части Assert, поскольку вы используете их для проверки того, произошло ли определенное взаимодействие.
Этот метод используется в определенном стиле Test-Driven Development, который называется наружным - в TDD (также называемом London School TDD). Это позволяет вам полностью реализовать класс и указать контракты для своих сотрудников без фактического внедрения самих соавторов, а просто создания интерфейсов.Его также можно использовать в других сценариях, например, когда вы тестируете устройство и не хотите работать с реальной базой данных, сетевым трафиком, зависимым от третьей стороны и/или классом, который просто ужасно трудный или медленный в использовании тесты.
Для получения дополнительной информации по этим темам, отметьте сообщение Мартина Фаулера на странице Mocks Aren't Stubs.