Объекты предметной области модульного тестирования
-
19-09-2019 - |
Вопрос
У нас есть требование добавить напоминание о событии, когда пользователь вводит свой адрес электронной почты на странице мероприятия.Событие — это еще один объект домена.Нашей первоначальной мыслью было создать объект домена Customer и связанный с ним CustomerService:
public class CustomerService {
public void AddEventReminder(string emailAddress, int eventId) {
var customer = new Customer(emailAddress);
customer.AddEmailReminder(eventId);
}
}
Как мы можем проверить с помощью модульного теста, что метод AddEmailReminder действительно был вызван для нового клиента?
Мои мысли:
- Используйте фабрику для создания клиента.Это пахнет, потому что я думал, что фабрику следует использовать только там, где есть некоторая сложность в создании объекта.
- Плохой код.Может быть, есть лучший способ сделать это?
- Магия Мок.
Отдельное замечание (возможно, это связано): как нам определить, какой здесь совокупный корень?Мы произвольно решили, что это клиент, но в равной степени это может быть и событие.Я прочитал и понял статьи об совокупных корнях, но в этом сценарии это неясно.
Решение
В подобных случаях я бы создал защищенный метод в службе, которая создает клиента, в тесте переопределил бы этот метод с помощью анонимного внутреннего класса и заставил бы его возвращать фиктивный объект Customer.Затем вы можете проверить на макете объекта Customer, что был вызван AddEmailReminder.Что-то вроде:
public class CustomerService {
public void AddEventReminder(string emailAddress, int eventId) {
var customer = createCustomer(emailAddress);
customer.AddEmailReminder(eventId);
}
protected Customer createCustomer(string emailAddress) {
return new Customer(emailAddress);
}
}
и в тесте (предположим, что знание C# ограничено, но это должно проиллюстрировать суть):
void testCustomerCreation() {
/* final? */ Customer mockCustomer = new Customer("email");
CustomerService customerService = new CustomerService() {
protected Customer createCustomer(string emailAddress) {
return mockCustomer;
}
};
customerService.AddEventReminder("email", 14);
assertEquals(mockCustomer.EventReminder() /* ? */, 14);
}
Другие советы
Мысли об API CustomerService
Есть ли какие-то особые причины, по которым вы решили инкапсулировать эту операцию в CustomerService?Это выглядит немного Анемичный мне.Можно ли вместо этого инкапсулировать его непосредственно на Заказчике?
Возможно, вы упустили что-то из примера кода CustomerService, чтобы упростить ситуацию...
Однако, если необходимо, изменение подписи для получения экземпляра Customer решает проблему:
public void AddEventReminder(Customer customer, int eventId)
но опять же, Int32 вряд ли можно квалифицировать как объект домена, поэтому подпись действительно должна быть
public void AddEventReminder(Customer customer, Event event)
Вопрос теперь в том, добавляет ли этот метод вообще какую-либо ценность?
Что такое совокупный корень?
Думаю, ни один из них.Агрегатный корень указывает, что вы управляете дочерними элементами. только через корень, и в данном случае это не имеет смысла в любом случае.
Рассмотрим варианты:
Если вы сделаете Event корневым, это будет означать, что у вас не может быть CustomerRepository, и единственный способ получить, отредактировать и сохранить Customer — через Event.Для меня это звучит очень неправильно.
Если вы сделаете Customer корневым, у вас не будет EventRepository, и единственный способ получить, отредактировать и сохранить событие — через конкретного Customer.Для меня это звучит так же неправильно.
Единственная оставшаяся возможность состоит в том, что это отдельные корни.Это также означает, что они слабо связаны друг с другом и что вам понадобится какая-то служба домена для поиска событий для клиента или клиентов для события.