Ответ 1
Из того, что я понимаю об этом сценарии, мне нужен интерфейс IEmployeeManager, который используется только для целей тестирования. Это не кажется правильным, поскольку интерфейс не имеет другого смысла.
Хорошо стоит создать интерфейс. Также обратите внимание, что интерфейс фактически имеет несколько целей:
- Интерфейс идентифицирует роли или обязанности, предоставляемые актером. В этом случае интерфейс идентифицирует роли и обязанности
EmployeeManager
. Используя интерфейс, вы предотвращаете случайную зависимость от конкретной базы данных. - Интерфейс уменьшает связь. Поскольку ваше приложение не будет зависеть от
EmployeeManager
, вы можете поменять его реализацию, не перекомпилируя остальную часть приложения. Конечно, это зависит от структуры проекта, количества сборок и т.д., Но тем не менее позволяет использовать этот тип повторного использования. - Интерфейс способствует тестируемости.. Когда вы используете интерфейс, гораздо проще создавать динамические прокси, которые позволяют более легко тестировать ваше программное обеспечение.
- Интерфейс заставляет мысли 1. Хорошо, я уже упоминал об этом, но это стоит повторить. Просто используя только интерфейс, вы должны подумать о ролях и обязанностях объектов. Интерфейс не должен быть кухонной раковиной. Интерфейс представляет собой сплоченный набор ролей и обязанностей. Если методы интерфейса не являются сплоченными или почти не используются вместе, то, вероятно, объект имеет несколько ролей. Хотя это и не обязательно плохо, это означает, что несколько различных интерфейсов лучше. Чем больше интерфейс, тем сложнее сделать его ковариантным или контравариантным и, следовательно, более податливым в коде.
Тем не менее, это позволит мне создать класс тестов EmployeeManager, который загружает сотрудников без привлечения базы данных... Я не понимаю. Почему макет, когда я могу просто создать простой тестовый класс из IEmployeeManager, который предоставит мне то, что мне нужно?
Как отметил один плакат, похоже, что вы говорите о создании тестового класса-заглушки. Для создания заглушек можно использовать фальшивые фреймворки, но одна из наиболее важных особенностей их заключается в том, что они позволяют вам тестировать поведение вместо состояния. Теперь рассмотрим несколько примеров. Предположим следующее:
interface IEmployeeManager {
void AddEmployee(ProspectiveEmployee e);
void RemoveEmployee(Employee e);
}
class HiringOfficer {
private readonly IEmployeeManager manager
public HiringOfficer(IEmployeeManager manager) {
this.manager = manager;
}
public void HireProspect(ProspectiveEmployee e) {
manager.AddEmployee(e);
}
}
Когда мы тестируем поведение HiringOfficer
HireEmployee
, мы заинтересованы в проверке того, что он правильно передал менеджеру сотрудника, чтобы этот перспективный сотрудник был добавлен в качестве сотрудника. Вы часто увидите что-то вроде этого:
// you have an interface IEmployeeManager and a stub class
// called TestableEmployeeManager that implements IEmployeeManager
// that is pre-populated with test data
[Test]
public void HiringOfficerAddsProspectiveEmployeeToDatabase() {
var manager = new TestableEmployeeManager(); // Arrange
var officer = new HiringOfficer(manager); // BTW: poor example of real-world DI
var prospect = CreateProspect();
Assert.AreEqual(4, manager.EmployeeCount());
officer.HireProspect(prospect); // Act
Assert.AreEqual(5, manager.EmployeeCount()); // Assert
Assert.AreEqual("John", manager.Employees[4].FirstName);
Assert.AreEqual("Doe", manager.Employees[4].LastName);
//...
}
Вышеуказанный тест является разумным... но не очень хорошим. Это государственный тест. То есть, он проверяет поведение, проверяя состояние до и после некоторого действия. Иногда это единственный способ проверить вещи; иногда это лучший способ проверить что-то.
Но, поведение тестирования часто бывает лучше, и это то, где насмехаются фреймворки:
// using Moq for mocking
[Test]
public void HiringOfficerCommunicatesAdditionOfNewEmployee() {
var mockEmployeeManager = new Mock<EmployeeManager>(); // Arrange
var officer = new HiringOfficer(mockEmployeeManager.Object);
var prospect = CreateProspect();
officer.HireProspect(prospect); // Act
mockEmployeeManager.Verify(m => m.AddEmployee(prospect), Times.Once); // Assert
}
В приведенном выше примере мы проверили единственное, что действительно имело значение, - что сотрудник по найму связался с менеджером-сотрудником, который должен был добавить новый сотрудник (один раз и только один раз... хотя я действительно не стал бы проверять проверку счет в этом случае). Мало того, что я подтвердил, что сотрудник, который я попросил нанять сотрудника по найму, был добавлен менеджером сотрудника. Я проверил критическое поведение. Мне не нужен даже простой тестовый заглушка. Мой тест был короче. Фактическое поведение было гораздо более очевидным - становится возможным видеть взаимодействие и проверять взаимодействие между объектами.
Можно сделать ваши записи в тестовом классе, но тогда вы эмулируете насмешливые рамки. Если вы собираетесь тестировать поведение - используйте насмешливую структуру.
В качестве другого упоминаемого плаката важны инъекции зависимостей (DI) и инверсия контроля (IoC). Мой пример выше не является хорошим примером этого, но оба должны быть тщательно рассмотрены и разумно использованы. Там много запись на тема доступно.
1 - Да, мышление по-прежнему необязательно, но я настоятельно рекомендую его;).