ほとんどのサービスが単にdaosに渡され、daosがほとんど何もしないがSpringのメソッドを呼び出す場合、HibernateTemplate
またはJdbcTemplate
ユニットテストは統合テストがすでに証明していることを実際には証明しないというのは正しいことです。ただし、通常の理由から、単体テストを実施することは価値があります。
単体テストは単一のクラスのみをテストし、ディスクやネットワークにアクセスせずにメモリ内で実行し、実際に一緒に動作する複数のクラスをテストすることはないため、通常は次のようになります。
- サービスユニットテストは、daosをモックします。
- Daoユニットテストは、データベースドライバー(またはSpringテンプレート)をモックするか、組み込みデータベースを使用します(Spring 3では非常に簡単です)。
daoに渡されるサービスを単体テストするには、次のようにモックすることができます。
@Before
public void setUp() {
service = new EventServiceImpl();
dao = mock(EventDao.class);
service.EventDao = dao;
}
@Test
public void creationDelegatesToDao() {
service.createEvent(sampleEvent);
verify(dao).createEvent(sampleEvent);
}
@Test(expected=EventExistsException.class)
public void creationPropagatesExistExceptions() {
doThrow(new EventExistsException()).when(dao).createEvent(sampleEvent);
service.createEvent(sampleEvent);
}
@Test
public void updatesDelegateToDao() {
service.updateEvent(sampleEvent);
verify(dao).updateEvent(sampleEvent);
}
@Test
public void findingDelgatesToDao() {
when(dao.findEventById(7)).thenReturn(sampleEvent);
assertThat(service.findEventById(7), equalTo(sampleEvent));
service.findEvents("Alice", 1, 5);
verify(dao).findEventsByName("Alice", 1, 5);
service.findEvents(null, 10, 50);
verify(dao).findAllEvents(10, 50);
}
@Test
public void deletionDelegatesToDao() {
service.deleteEvent(sampleEvent);
verify(dao).deleteEvent(sampleEvent);
}
しかし、これは本当に良い考えですか?これらのMockitoアサーションは、daoメソッドが呼び出されたと主張しており、期待どおりに実行されたとは主張していません。あなたはあなたのカバレッジ数を得るでしょう、しかしあなたは多かれ少なかれあなたのテストをdaoの実装に拘束しています。痛い。
この例では、サービスに実際のビジネスロジックがないと想定しています。通常、サービスにはdao呼び出しに加えてビジネスロジックがあり、それらをテストする必要があります。
さて、daosの単体テストには、組み込みデータベースを使用するのが好きです。
private EmbeddedDatabase database;
private EventDaoJdbcImpl eventDao = new EventDaoJdbcImpl();
@Before
public void setUp() {
database = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql")
.addScript("init.sql")
.build();
eventDao.jdbcTemplate = new JdbcTemplate(database);
}
@Test
public void creatingIncrementsSize() {
Event e = new Event(9, "Company Softball Game");
int initialCount = eventDao.findNumberOfEvents();
eventDao.createEvent(e);
assertThat(eventDao.findNumberOfEvents(), is(initialCount + 1));
}
@Test
public void deletingDecrementsSize() {
Event e = new Event(1, "Poker Night");
int initialCount = eventDao.findNumberOfEvents();
eventDao.deleteEvent(e);
assertThat(eventDao.findNumberOfEvents(), is(initialCount - 1));
}
@Test
public void createdEventCanBeFound() {
eventDao.createEvent(new Event(9, "Company Softball Game"));
Event e = eventDao.findEventById(9);
assertThat(e.getId(), is(9));
assertThat(e.getName(), is("Company Softball Game"));
}
@Test
public void updatesToCreatedEventCanBeRead() {
eventDao.createEvent(new Event(9, "Company Softball Game"));
Event e = eventDao.findEventById(9);
e.setName("Cricket Game");
eventDao.updateEvent(e);
e = eventDao.findEventById(9);
assertThat(e.getId(), is(9));
assertThat(e.getName(), is("Cricket Game"));
}
@Test(expected=EventExistsException.class)
public void creatingDuplicateEventThrowsException() {
eventDao.createEvent(new Event(1, "Id1WasAlreadyUsed"));
}
@Test(expected=NoSuchEventException.class)
public void updatingNonExistentEventThrowsException() {
eventDao.updateEvent(new Event(1000, "Unknown"));
}
@Test(expected=NoSuchEventException.class)
public void deletingNonExistentEventThrowsException() {
eventDao.deleteEvent(new Event(1000, "Unknown"));
}
@Test(expected=NoSuchEventException.class)
public void findingNonExistentEventThrowsException() {
eventDao.findEventById(1000);
}
@Test
public void countOfInitialDataSetIsAsExpected() {
assertThat(eventDao.findNumberOfEvents(), is(8));
}
ほとんどの人が統合テストと呼ぶかもしれませんが、私はまだこれを単体テストと呼んでいます。組み込みデータベースはメモリ内にあり、テストの実行時に起動および停止されます。ただし、これは、組み込みデータベースが本番データベースと同じように見えるという事実に依存しています。そうなるのでしょうか?そうでなければ、そのすべての作業はかなり役に立たなかった。もしそうなら、あなたが言うように、これらのテストは統合テストとは異なることをしています。しかし、私はそれらをオンデマンドで実行することができmvn test
、リファクタリングする自信があります。
そのため、とにかくこれらの単体テストを作成し、カバレッジ目標を達成します。統合テストを作成するとき、HTTPリクエストが期待されるHTTPレスポンスを返すことを主張します。ええ、それはユニットテストを含みます、しかしねえ、あなたがTDDを練習するとき、あなたはとにかくあなたの実際のdao実装の前にそれらのユニットテストを書いています。
daoの後に単体テストを作成する場合は、もちろん、作成するのは楽しいことではありません。TDDの文献には、コードが機能するように感じられ、誰もそれをやりたくないと感じた後にテストを書く方法についての警告がたくさんあります。
TL; DR:統合テストは単体テストを包含します。その意味で、単体テストは実際のテスト値を追加していません。ただし、カバレッジの高いユニットテストスイートを使用している場合は、リファクタリングを行う自信があります。しかしもちろん、daoがSpringのデータアクセステンプレートを簡単に呼び出している場合は、リファクタリングを行っていない可能性があります。しかし、あなたは決して知りません。そして最後に、ユニットテストが最初にTDDスタイルで記述されている場合は、とにかくそれらを使用することになります。