В вашем примере вы тестируете публичный метод Traffic
.То, что Traffic
реализует ITraffic
, не имеет значения.Если вы удалили : ITraffic
из класса, чтобы он больше не реализовывал этот интерфейс, это не изменит того, как вы тестировали Traffic
.
Вы тестируете Traffic
.Мы не высмеиваем то, что тестируем.Мы высмеиваем то, что мы не тестируем.
Допустим, у меня есть этот класс, который проверяет адрес:
public class AddressValidator
{
public ValidationResult ValidateAddress(Address address)
{
var result = new ValidationResult();
if(string.IsNullOrEmpty(address.Line1))
result.AddError("Address line 1 is empty.");
if(string.IsNullOrEmpty(address.City))
result.AddError("The city is empty.");
// more validations
return result;
}
}
Не имеет значения, является ли этот классреализует интерфейс или нет.Если я тестирую этот класс, то нечего издеваться.
Предположим, я понимаю, что мне нужно также проверить почтовый индекс, но для этого мне нужно, возможно, запросить некоторые внешние данные, чтобы увидеть, если городсоответствует почтовому индексуМожет быть, это разные для разных стран.Поэтому я пишу новый интерфейс и внедряю его в этот класс:
public interface IPostalCodeValidator
{
ValidationResult ValidatePostalCode(Address address);
}
public class AddressValidator
{
private readonly IPostalCodeValidator _postalCodeValidator;
public AddressValidator(IPostalCodeValidator postalCodeValidator)
{
_postalCodeValidator = postalCodeValidator;
}
public ValidationResult ValidateAddress(Address address)
{
var result = new ValidationResult();
if (string.IsNullOrEmpty(address.Line1))
result.AddError("Address line 1 is empty.");
if (string.IsNullOrEmpty(address.City))
result.AddError("The city is empty.");
var postalCodeValidation = _postalCodeValidator.ValidatePostalCode(address);
if (postalCodeValidation.HasErrors)
result.AddErrors(postalCodeValidation.Errors);
return result;
}
}
Проверка почтового кода достаточно сложна, так что он будет в своем собственном классе со своими собственными тестами.Когда мы тестируем AddressValidator
, мы не хотим проверить валидатор почтового индекса.Мы просто хотим протестировать этот класс изолированно, а другой класс отдельно.В пределах AddressValidator
мы хотим убедиться, что вызывается _postalCodeValidator.ValidatePostalCode
, и что если он возвращает ошибки, мы добавляем их в результат проверки.
Мы не тестируем IPostalCodeValidator
(или его реализации) здесь, поэтому мы высмеиваем это.Например, используя Moq :
public void AddressValidator_adds_postal_code_errors()
{
var postalCodeError = new ValidationResult();
postalCodeError.AddError("Bad!");
postalCodeError.AddError("Worse!");
var postalCodeValidatorMock = new Mock<IPostalCodeValidator>();
postalCodeValidatorMock.Setup(x => x.ValidatePostalCode(It.IsAny<Address>()))
.Returns(postalCodeError);
var subject = new AddressValidator(postalCodeValidatorMock.Object);
var result = subject.ValidateAddress(new Address());
Assert.IsTrue(result.Errors.Contains("Bad!"));
Assert.IsTrue(result.Errors.Contains("Worse!"));
}
На самом деле мы не проверяем почтовый индекс.Мы просто говорим, что для проверки валидатор почтового кода всегда будет возвращать эти две ошибки.Затем мы удостоверимся, что AddressValidator
вызывает его и делает то, что мы ожидаем, что будет делать с этими ошибками.
Это в основном то, что макет.Это фальшивая реализация чего-то, что делает что-то простое, например, постоянный ответ, так что мы можем быть уверены, что обработаем этот постоянный ответ так, как мы ожидаем.Если AddressValidator
обрабатывает результат правильно, то он работает правильно.Это сделано.
Чтобы убедиться, что валидатор почтового кода real возвращает правильный результат, мы можем написать тесты для этого класса.Таким образом, каждый класс делает что-то простое и имеет тесты, чтобы убедиться, что он делает свое дело правильно.Когда мы соберем их все вместе, гораздо более вероятно, что все это сработает.Если мы нарушим реализацию из IPostalCodeValidator
, то тесты для этого класса не пройдут, но тесты для AddressValidator
все равно пройдут.Таким образом, мы быстро понимаем, какая часть сломалась, потому что все они тестируются изолированно, поэтому нам не нужно запускать и отлаживать большое количество кода, пытаясь выяснить, в чем проблема.