Это делается для того, чтобы вы могли внедрить тестовый двойник класса IRepository при модульном тестировании бизнес-уровня. Это имеет следующие преимущества:
- Это позволяет вам легко определить, что неудачные тесты вызваны бизнес-уровнем, а не уровнем репозитория;
- Это делает ваши тесты уровня бизнес-логики быстрыми, поскольку они не зависят ни от доступа к данным, который, как правило, медленный, ни от настройки структуры базы данных и тестовых данных, которые, как правило, очень медленные.
Один из способов внедрить тест удваивается при модульном тестировании с помощью внедрения конструктора. Предположим, что в вашем репозитории есть следующие методы:
void Add(Noun noun);
int NumberOfNouns();
А это код вашего бизнес-класса:
public class BusinessClass {
private IRepository _repository;
public BusinessClass(IRepository repository) {
_repository = repository;
}
// optionally, you can make your default constructor create an instance
// of your default repository
public BusinessClass() {
_repository = new Repository();
}
// method which will be tested
public AddNoun(string noun) {
_repository.Add(new Noun(noun));
}
}
Чтобы протестировать AddNoun, не нуждаясь в реальном репозитории, вам нужно настроить тестовый двойник. Обычно вы делаете это, используя фиктивный фреймворк, такой как Moq, но я напишу фиктивный класс с нуля, просто чтобы проиллюстрировать концепцию.
public IRepository MockRepository : IRepository {
private List<Noun> nouns = new List<Noun>();
public void Add(Noun noun) {
nouns.Add(noun);
}
public int NumberOfNouns() {
return nouns.Count();
}
}
Теперь одним из ваших тестов может быть это.
[Test]
public void AddingNounShouldIncreaseNounCountByOne() {
// Arrange
var mockRepository = new MockRepository();
var businessClassToTest = new BusinessClass(mockRepository);
// Act
businessClassToTest.Add("cat");
// Assert
Assert.AreEqual(1, mockRepository.NumberOfNouns(), "Number of nouns in repository should have increased after calling AddNoun");
}
Это позволило вам протестировать функциональность вашего метода BusinessClass.AddNoun без необходимости прикасаться к базе данных. Это означает, что даже если есть проблема с вашим уровнем репозитория (например, проблема со строкой подключения), вы можете быть уверены, что ваш бизнес-уровень работает должным образом. Это относится к пункту 1 выше.
Что касается пункта 2 выше, всякий раз, когда вы пишете тесты, проверяющие базу данных, вы должны убедиться, что она находится в известном состоянии перед каждым тестом. Обычно это включает удаление всех данных в начале каждого теста и повторное добавление тестовых данных. Если этого не сделать, вы не сможете запускать утверждения, скажем, для количества строк в таблице, потому что вы не будете уверены, что это должно быть.
Удаление и повторное добавление тестовых данных обычно выполняется с помощью сценариев SQL, которые медленны и уязвимы при каждом изменении структуры базы данных. Поэтому рекомендуется ограничить использование базы данных только тестами самого репозитория и использовать макеты репозиториев при модульном тестировании других аспектов приложения.
Что касается использования абстрактных классов - да, это дало бы такую же возможность предоставления тестовых двойников. Я не уверен, какой код вы бы выбрали для абстрактной базы, а какой конкретную реализацию. В этом ответе на вопрос SO есть интересное обсуждение абстрактного классы против интерфейсов.
person
Polly Shaw
schedule
04.08.2013