Junit / Mockito: выбор запуска теста с использованием имитаций или интеграционных тестов

Я изучаю Мокито. Перед тем, как начать использовать фиктивные объекты, у меня было несколько модульных тестов, которые больше походили на интеграционные тесты, поэтому у меня был тестовый класс с setUpBeforeClass () следующим образом:

@BeforeClass
public static void setUpBeforeClass() throws Exception {
    instance = new UserDataAccess();
    instance.setDb(new MyDb());
}

Теперь с mock Object во многом похоже, но настройка немного сложнее:

@BeforeClass
public static void setupBeforeClass throws Exception {
    instance = new UserDataAccess();
    MyDb myDb = mock(MyDb.class);
    when(...).thenReturn(...);
    ...
    instance.setDb(myDb);
}

Также у меня есть набор тестов, который используется для загрузки БД в известном состоянии перед запуском тестов, и это делается с помощью первого тестового класса, вызываемого набором.

Я думаю, что мне не следует выбрасывать интеграционные тесты, поэтому я подумал о разделении набора тестов на UnitTestSuite и IntegrationTestSuite. Фактически, макетные тесты не тестируют все, например, они не проверяют правильность запросов.

Кроме того, единственной разницей между этими двумя наборами будет исходный сброс БД и код setUpBeforeClass (). Было бы бесполезно копировать и изменять все тестовые классы только для того, чтобы изменить метод. Первоначальный сброс БД легко пропустить, я просто не включаю тестовый класс сброса БД в набор модульных тестов.

Что вы предлагаете для разделения модульного и интеграционного тестов? Расширение всех исходных классов для переопределения статического метода с последующим включением нужного класса в набор?

Или другие подходы? Как вы это делаете или что бы вы сделали?


person stivlo    schedule 05.07.2011    source источник


Ответы (2)


Всегда помните, что идея модульных тестов (с использованием имитаций) состоит в том, чтобы выискивать и проверять все темные углы одного класса. Они о том, чтобы убедиться, что класс ведет себя так, как ожидалось, для всех видов ввода, о которых вы можете подумать. Вот почему мы используем mocks для этого, потому что мы можем запрограммировать эти mocks для выполнения всевозможных вещей, которые было бы трудно воспроизвести, если бы класс был подключен к остальной части приложения.

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

Я также согласен с @stivlo в использовании таких продуктов, как HSQL, для работы с базами данных в памяти. Они могут как ускорить интеграционное тестирование, так и помочь обеспечить известную отправную точку для тестирования.

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

Также следите за тестовой ползучестью. Т.е. Модульные тесты, которые создают экземпляры множества классов приложений и действительно должны быть перемещены в интеграционные тесты, и интеграционные тесты, которые выполняют множество подробных тестов вариативного стиля для класса - кандидатов для модульного теста. В частности, с последним вы можете сократить много времени на сборку, если есть интеграционные тесты, которых не должно быть.

Наконец, как и любой другой код, тестовый код время от времени требует немного любви и внимания. Так что следите за возможностями рефакторинга. При этом я также видел тестовый код, с которым стало трудно работать, потому что он слишком обобщен и чрезмерно спроектирован. Помните, что тестовый код - это не производственный код, поэтому в большинстве случаев ясность намного важнее инженерной мысли. Это баланс, который вы достигнете благодаря опыту.

person drekka    schedule 05.07.2011
comment
Проголосовали за абзац о тестовой ползучести. Интеграционные тесты и модульные тесты обычно различаются по большему счету, чем использование моков; Модульные тесты охватывают контракт одного класса, интеграционные тесты охватывают (части) вариантов использования в отношении всей системы. - person Adriaan Koster; 05.07.2011
comment
Спасибо, Дерек, поэтому на практике вы говорите, что, возможно, не стоит запускать те же тесты, что и интеграция и модульное тестирование, просто отменяя метод, потому что я бы пропустил другой акцент тестов, и я ' Я, возможно, излишне спроектировал тест, так что я мог бы также обменять это на некоторое дублирование кода. Это правильно? - person stivlo; 05.07.2011
comment
Ага, в значительной степени. Я уверен, что некоторые не согласятся с дублированием кода, но я не возражаю против небольшого дублирования, если это помогает прояснить ситуацию. Это очень сильно зависит от ваших тестов. Если вы не можете прочитать и понять их, не потратив 30 минут на изучение кодовой базы, стоящей за ними, значит, они слишком сложны. С другой стороны, если вы видите, что одна и та же настройка выполняется 4 или 5 раз в классе, содержащем модульные тесты, то ее, вероятно, можно реорганизовать в абстрактный метод или метод setup (). Как я уже сказал, это личный звонок, основанный на коде. Лучший гид - KISS. - person drekka; 05.07.2011

Я бы предложил использовать базу данных в памяти для интеграционных тестов, а не фиктивный объект.

Для модульного теста я предполагаю, что вы используете базу данных как заглушку для предоставления некоторых данных в тесты и как имитацию, когда ваш тест вызывает что-то вроде saveMyDomainObject(). В первом случае вы можете смоделировать только то, что вам действительно нужно для конкретного теста, а не всю настройку базы данных. Во втором случае вы должны использовать Mockito validate, чтобы проверить правильность ожидаемого поведения.

person denis.solonenko    schedule 05.07.2011
comment
Ну, на самом деле я использую схему тестовой базы данных для интеграционных тестов, которую я перезагружаю при каждом тестовом запуске (реальная база данных содержит миллионы записей, но у меня только несколько тысяч в тестовой базе данных, поэтому она не занимает длинная). Я использую или, что лучше, начинаю использовать фиктивные объекты для модульных тестов. Преимущество использования БД в памяти могло бы быть немного большей скоростью, но означало бы дополнительные усилия для устранения несовместимости SQL, плюс означало бы, что я больше не тестирую на реальной платформе, поэтому он может также работать с memory db и потерпите неудачу на реальной платформе из-за различий в SQL. - person stivlo; 05.07.2011