четверг, 12 ноября 2015 г.

Тесты на уровне API и почему их должны писать девелоперы

Пообщался сегодня с одним из наших архитекторов. Очень интересный разговор получился. Его месседж был следующим:

«Тесты должны выполнять операции так как они заимплементированы в коде, без всяких там врапперов для них»

Обоснование было таким:

«За врапперами скрывается реальная имплементация, что может привести к багам в тестах, если врапперы не повторяют на 100% того, как выполняется операция»

Очень сложно не согласиться с этой точкой зрения. Тест должен быть правильным, должен выполнять операции так, как они заимплементированы. Когда мы пишем UI тесты, так и происходит, т.к. мы оперируем лишь с контролами, и уже они вызывают операции. Но когда тесты выполняются на уровне API, все становится уже не так просто.

Приведу небольшой пример:

Нужен был тест, который выполняет операцию рассчета некоторых данных и потом уже проверяет её. Пишем следующий код:

public void testCalculation(int param1, int param2) {
     Operation operation = new CalculateOperation(param1,  param2);
     assertTrue(operation.execute(), “Execution failed”);
}

Вроде все ОК, но потом кто-то тестом находит «баг», что для некоторых значений калькуляция не работает. А тест этого не находит. Почему же? Копаем код и оказывается, что в приложении перед калькуляцией происходит валидация данных. Добавляем:

public void testCalculation(int param1, int param2) {
     Operation operation = new CalculateOperation(param1,  param2);
     assertTrue(operation.validate(), “Validation failed”);
     assertTrue(operation.execute(), “Execution failed”);
}

Обращаем внимание, что подобная структура присутствует в нескольких тестовых методах, решаем вынести это в отдельный метод:

public boolean runOperation(Operation operation) {
     assertTrue(operation.validate(), “Validation failed”);
     return operation.execute();
}
public void testCalculation(int param1, int param2) {
     Operation operation = new CalculateOperation(param1,  param2);
     assertTrue(runOperation (operation), “Execution failed”);
}

Выглядит вроде хорошо – все довольны. Но потом оказывается, что некоторые калькуляции надо запускать без валидаций, создаем еще один враппер.

public boolean runOperationWithValidation(Operation operation) {
     assertTrue(operation.validate(), “Validation failed”);
     return operation.execute();
}
public boolean runOperationWithoutValidation(Operation operation) {
     return operation.execute();
}

Потом появляются дополнительные условия, и у нас уже 3-4-5 врапперов для запуска операции. И тут мы начинаем задумывать, какой именно враппер нам использвовать. Приходится лезть глубоко в код и разбираться в имплементации той или иной операции, чтобы выбрать в итоге нужный враппер.

Все может быть и нормально, когда у вас код написан на одном языке. А если их несколько? Если операци написана на C++, а вызывается из С# или какого javascript кода?

К чему я это все веду – не тестровщицкое это дело писать тесты на уровне API. На уровне UI – пишите, вы хозяева UI тестирования. Если же у вас есть желание разобраться с кодом приложения, вам это нравится, и у вас это хорошо получается, то дам вам совет – ищите работу девелопера (Software Developer), на худой конец, если без тестирования вы никак не можете, то тест девелопера (Software Developer in Test).

Тестирование - это не про код, это про работу приложения.

Отправить комментарий

Условия копирования публикаций:

Все публикации в данном блоге являются частной собственностью авторов. Любое копирование информации допускается только при условии указания имени автора и активной ссылки на источник.