... демонстрация выгоды какой-либо технологии на реальном примере
всегда затруднена из-за объема сопровождающих сведений, а если проблему редуцировать
до ее ядра, то она сама собой исчезает и кажется что огород вообще не стоит усилий.
всегда затруднена из-за объема сопровождающих сведений, а если проблему редуцировать
до ее ядра, то она сама собой исчезает и кажется что огород вообще не стоит усилий.
Продолжение поста о неванильном тестировании. Полный текст примеров: исходный код, тестовый фреймворк, тестовый сценарий. Данный пример повторяет реальную архитектуру, имеется мешанина с логикой, хотя там и написан полный бред. Плюс работа с базой лишь обозначена, а в тесте замокана для удобства. Тест написан только один, просто чтобы продемонстрировать подход - остальные тесты один-в-один.
Workflow
1. CollectorDaemon
На первом этапе FreeSWITCH шлет сырые события, внешне представляющие собой мультимап, хотя ни разу не встречалось более одного значения в ключе, видимо добавили на всякий случай.
Демоны CallCollectorDaemon и ConferenceCollectorDaemon (заглушка с логикой идентичной CCD) подписываются каждый на свою группу событий, после чего события начинают приходить в метод onEvent.
public CallCollectorDaemon(CallRouter router) {
this.router = router;
subscribe("CREATE", "BRIDGE", "DESTROY");
}
@Override
public void onEvent(HashMap<String, String> rawEvent) {
...
Задача демона, распарсить события в соответствующие DTO.
2. Router
Router хранит справочник всех ведущихся сессий, создает новые сессии, сливает разние сессии в одну, если они оказываются связанными. После того как подходящая сессия найдена, событие отправляется в сессию для извлечения данных.3. Call
Call - сессия, которая может породить в процессе несколько записей. При каждом поступившем событии данные извлекаются и проверяются два условия: на готовность записи и завершенность сессии.4-6. Router
Если в сессии после события оказывается готовая запись, то она извлекается роутером и заполняется данными из базыprivate void testCallReady(Call call) {
// Check for ready record
if (call.getReady().size() > 0) {
ready.addAll(call.getReady());
call.getReady().clear();
}
...
}
...
public Record pullRecord(UserDao userDao) {
Record record = ready.pollFirst();
...
User user = userDao.findUserByName(record.getNumber());
if (user == null) {
user = userDao.findUserByTerminal(record.getNumber());
}
if (user != null) {
record.setUser(user);
}
return record;
}
7. CollectorDaemon
Демон окончательно сохраняет готовые записи в базу, он же управляет соединением с базой.
Тестовый фреймворк
Тестер
Тестер работает только со вторым слоем, потому, что тестировать первый нет особого смысла. Сначала загружается и парсится yaml файл при помощи snakeyaml:
protected List<Object> loadEvents(String yaml) {
InputStream is = getClass().getClassLoader().getResourceAsStream(yaml);
return (List<Object>) new Yaml().load(is);
}
Затем объекты засылаются в роутер если это событие, либо сравниваются с готовыми записями, если это запись:
protected void testEventFlow(List<Object> events) {
HashMap<Long, User> userHashMap = new HashMap<Long, User>();
for (User u : userDao.findAll()) {
userHashMap.put(u.getId(), u);
}
CallRouter er = new CallRouter();
for (Object event : events) {
if (event instanceof CreateEvent) {
er.addEvent((CreateEvent) event);
} else if (event instanceof BridgeEvent) {
er.addEvent((BridgeEvent) event);
} else if (event instanceof DestroyEvent) {
er.addEvent((DestroyEvent) event);
} else if (event instanceof Record) {
Record record = er.pullRecord(userDao);
((Record) event).setUser(userHashMap.get(((Record) event).getUser().getId()));
compare((Record) event, record);
}
}
assertFalse(er.hasNewRecord());
}
Для удобства сравнения, поле юзер подменяется по первичному ключу.
Сценарий
Представляет собой типичный yaml файл:
- !!com.blazer.scenario.event.DestroyEvent
id: 541852
chain: 1239641
date: 1333291427000000
service: USER
name: terminal2
hangup: !!com.blazer.scenario.domain.HangupType NORMAL
- !!com.blazer.scenario.domain.Record
id: 541852
chain: 1239641
begin: 1333291338000000
end: 1333291427000000
user: !!com.blazer.scenario.domain.User
id: 2
number: terminal2
legB: 541852
hangup: !!com.blazer.scenario.domain.HangupType NORMAL
Очень хрупкий, но, тем не менее довольно легко представляющий необходимый объект с любым уровнем иерархии, и даже циклическими ссылками.
Проблемные места
| When we have to simulate the software to test it |
