サービスレイヤーの脆弱なテストを回避する
目次
1. 概要
アプリケーションのサービスレイヤーをテストする方法はたくさんあります。 この記事の目的は、データベースとの相互作用を完全にモックアウトすることにより、このレイヤーを単独で単体テストする1つの方法を示すことです。
この例では、依存性注入に Spring 、テストに Hamcrest 、 Mockito を使用しますが、テクノロジーは異なる場合があります。
2. レイヤー
一般的なJavaWebアプリケーションには、DAL / DAOレイヤーの上にサービスレイヤーがあり、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. 結論
この手法は、常により焦点を絞ったテストにつながり、変更に対する弾力性と柔軟性が向上します。 テストが失敗する唯一の理由は、テスト中の責任が壊れているためです。