2012-05-12 1 views
6

Я знаком с основными принципами TDD, являются:Каков наилучший подход для модульного тестирования, когда у вас есть интерфейсы с фиктивными и реальными реализациями?

  1. Писать тесты, они будут не в состоянии из-за отсутствия реализации
  2. Написать базовую реализацию, чтобы сделать тесты пройдены
  3. Refactor код

Однако я немного запутался относительно того, где сопряжены интерфейсы и реализация. Я создаю веб-приложение Spring в свое свободное время, и вместо того, чтобы идти в пушки, я хотел бы понять, как я могу лучше проверить интерфейсы/реализации, возьмите этот простой пример кода, который я здесь создал:

public class RunMe 
{ 

    public static void main(String[] args) 
    { 
     // Using a dummy service now, but would have a real implementation later (fetch from DB etc.) 
     UserService userService = new DummyUserService(); 
     System.out.println(userService.getUserById(1)); 
    } 
} 

interface UserService 
{ 
    public String getUserById(Integer id); 
} 

class DummyUserService implements UserService 
{ 
    @Override 
    public String getUserById(Integer id) 
    { 
     return "James"; 
    } 
} 

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

Вопрос: Как я могу реализовать стратегию тестирования для вышеуказанного?

я мог бы создать тестовый класс под названием DummyUserServiceTest и тест, когда я звоню getUserById() он будет возвращаться James, кажется довольно простым, если не пустая трата времени (?).

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

Вопрос (улучшенный, немного): При использовании интерфейсов с фиктивными/закодированными и реальными реализациями, какие части должны быть проверены на единицу, а какие части могут быть оставлены непроверенными?

Я провел несколько часов погуглить на эту тему прошлой ночью, и главным образом найдены либо учебники на том, что TDD является или примеры того, как использовать JUnit, но ничего в сферах консультирования, что на самом деле должны быть проверены. Это вполне возможно, хотя я не искал достаточно трудно или не искал правильную вещь ...

ответ

4

Не проверяйте фиктивные реализации: они не будут использоваться в производстве. Это не имеет смысла проверять их.

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

Обычная стратегия состоит в том, чтобы заполнить базу данных минимальными тестовыми данными в аннотированном методе теста @Before и проверить метод проверки того, что для идентификатора, который существует в базе данных, возвращается соответствующее имя пользователя.

0

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

При разработке оригинального теста не следует беспокоиться о том, чтобы выровнять UserService. На самом деле, продолжайте и напишите настоящую вещь. Следуйте следующим Kent Beck's 3 rules.

1) Заставьте это работать. 2) Сделайте все правильно. 3) Сделайте это быстро.

Ваш код будет иметь тесты, которые затем проверяют работу find by id. Как указано в JB, на этом этапе ваши тесты будут считаться интеграционными испытаниями. Как только они проходят, мы успешно выполнили шаг 1. Теперь посмотрим на дизайн. Это правильно? Измените любые запахи дизайна и проверьте шаг 2 в своем списке.

Для шага 3 нам нужно выполнить этот тест быстро. Мы все знаем, что интеграционные тесты медленны и подвержены ошибкам со всеми настройками управления транзакциями и базой данных. Как только мы знаем, что код работает, я обычно не беспокоюсь об интеграционных тестах. Именно в это время вы можете представить свое фиктивное обслуживание, эффективно превратив ваш интеграционный тест в единичный тест. Теперь, когда он никак не коснется базы данных, мы можем проверить шаг 3 из списка, потому что этот тест теперь очень быстрый.

Итак, каковы проблемы с этим подходом? Ну, многие скажут, что мне все еще нужен тест для базы данных UserService. Обычно я не соблюдаю интеграционные тесты в моем проекте. Мое мнение состоит в том, что эти типы тестов медленны, хрупки и не ловят достаточное количество логических ошибок в большинстве проектов, чтобы платить за себя.

Надеюсь, что это поможет!

Brandon

3

Я рекомендовал бы вам прочитать эту книгу первым: Growing Object-Oriented Software Guided by Tests Стив Freemand и Nat Прайса. Он отвечает на ваш вопрос и многие другие, связанные с TDD.

В вашем конкретном случае вы должны настроить свой RealUserService с помощью DB-адаптера, который будет выполнять реальные запросы БД. Сама услуга будет обслуживание, а не постоянство данных. Прочтите книгу, это поможет много :)

Смежные вопросы