... демонстрация выгоды какой-либо технологии на реальном примере
всегда затруднена из-за объема сопровождающих сведений, а если проблему редуцировать
до ее ядра, то она сама собой исчезает и кажется что огород вообще не стоит усилий.
всегда затруднена из-за объема сопровождающих сведений, а если проблему редуцировать
до ее ядра, то она сама собой исчезает и кажется что огород вообще не стоит усилий.
Продолжение поста о неванильном тестировании. Полный текст примеров: исходный код, тестовый фреймворк, тестовый сценарий. Данный пример повторяет реальную архитектуру, имеется мешанина с логикой, хотя там и написан полный бред. Плюс работа с базой лишь обозначена, а в тесте замокана для удобства. Тест написан только один, просто чтобы продемонстрировать подход - остальные тесты один-в-один.
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 |