Избегайте хрупких испытаний для сервисного уровня

Содержание

  • ссылка: #overview[ 1. Обзор]

  • ссылка: # шаблоны[ 2. Слои]

  • ссылка: #javaconfig[ 3. Мотивация и размытие линий блока

тестовое задание] ссылка: # заключение[ 4. ** заключение]

1. Обзор

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

В этом примере будут использоваться Spring для внедрения зависимости, JUnit, Hamcrest и Mockito для тестирования, но технологии могут отличаться.

2. Слои

Типичное Java-веб-приложение будет иметь слой службы поверх слоя DAL/DAO, который, в свою очередь, вызовет слой необработанного постоянства.

1.1. Сервисный уровень

@Service
public class FooService implements IFooService{

   @Autowired
   IFooDAO dao;

   @Override
   public Long create( Foo entity ){
      return this.dao.create( entity );
   }

}

1.2. Уровень DAL/DAO

@Repository
public class FooDAO extends HibernateDaoSupport implements IFooDAO{

   public Long create( Foo entity ){
      Preconditions.checkNotNull( entity );

      return (Long) this.getHibernateTemplate().save( entity );
   }

}

3. Мотивация и размытие линий модульного теста

При модульном тестировании службы стандартным модулем обычно является класс обслуживания , просто так. Тест будет макетировать слой под ним - в этом случае слой DAO/DAL и проверять взаимодействия на нем. Точно то же самое для уровня DAO - макетирование взаимодействий с базой данных ( HibernateTemplate в этом примере) и проверка взаимодействий с этим.

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

Чтобы избежать такого рода негибкости, мы можем расширить область применения модульного теста, изменив определение модуля - мы можем рассматривать постоянную операцию как единое целое, от уровня обслуживания через DAO и весь день до необработанного настойчивость - что бы это ни было. Теперь модульное тестирование будет использовать API сервисного уровня и будет отключено сырое постоянство - в данном случае HibernateTemplate :

public class FooServiceUnitTest{

   FooService instance;

   private HibernateTemplate hibernateTemplateMock;

   @Before
   public void before(){
      this.instance = new FooService();
      this.instance.dao = new FooDAO();
      this.hibernateTemplateMock = mock( HibernateTemplate.class );
      this.instance.dao.setHibernateTemplate( this.hibernateTemplateMock );
   }

   @Test
   public void whenCreateIsTriggered__thenNoException(){
     //When
      this.instance.create( new Foo( "testName" ) );
   }

   @Test( expected = NullPointerException.class )
   public void whenCreateIsTriggeredForNullEntity__thenException(){
     //When
      this.instance.create( null );
   }

   @Test
   public void whenCreateIsTriggered__thenEntityIsCreated(){
     //When
      Foo entity = new Foo( "testName" );
      this.instance.create( entity );

     //Then
      ArgumentCaptor< Foo > argument = ArgumentCaptor.forClass( Foo.class );
      verify( this.hibernateTemplateMock ).save( argument.capture() );
      assertThat( entity, is( argument.getValue() ) );
   }

}

Теперь тест фокусируется только на одной ответственности - когда срабатывает создание, достигает ли создание базы данных?

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

4. Заключение

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