2017-03-28 17 views
1

any()だけでなく、適切なオブジェクトをverifyメソッドに渡したいと思います。私は正しいPreparedStatementを受け取ったことを単体テストできますか?

これを行う方法はありますか?

ラムダメソッドを取得してコピーし、結果を検証に渡すことはできません。 Lambdaは直接テストすることができないため、これは機能しません。

明らかに何かをテストするにしても近接していない私のユニットテスト:

@Test 
public void testRunTrigger() { 
    campaignTrigger.updateCampaignStatus(); 

    verify(jdbcTemplate).update(any(PreparedStatementCreator.class)); 
    assertEquals("UPDATE campaign SET state = 'FINISHED' WHERE state IN ('PAUSED','CREATED','RUNNING') AND campaign_end < ? ", campaignTrigger.UPDATE_CAMPAIGN_SQL); 
} 

そして、これは私がテストしていたクラスです:

@Component 
@Slf4j 
public class CampaignTrigger { 
final String UPDATE_CAMPAIGN_SQL = String.format("UPDATE campaign SET state = '%s' " + 
       " WHERE state IN (%s) AND campaign_end < ? ", FINISHED, 
     Stream.of(PAUSED, CREATED, RUNNING) 
       .map(CampaignState::name) 
       .collect(Collectors.joining("','", "'", "'"))); 

@Autowired 
private JdbcTemplate jdbcTemplate; 

@Scheduled(cron = "${lotto.triggers.campaign}") 
@Timed 
void updateCampaignStatus() { 
    jdbcTemplate.update(con -> { 
     PreparedStatement callableStatement = con.prepareStatement(UPDATE_CAMPAIGN_SQL); 
     callableStatement.setTimestamp(1, Timestamp.valueOf(LocalDateTime.now())); 
     log.debug("Updating campaigns statuses."); 
     return callableStatement; 
    }); 
} 

これがあることを何かアドバイス、または理論的な知識それを行う方法ではない、私は非常に感謝します。

+0

適切なパラメータで 'con.prepareStatement(UPDATE_CAMPAIGN_SQL)'と 'callableStatement.setTimestamp(1、Timestamp.valueOf(LocalDateTime.now())')が呼び出されたことを確認できます。 – Morfic

+0

もっと提供してください詳細はどうすればいいですか? – Amiko

+0

そのようなものは 'con'が模擬であるかどうかに依存します。同時に、@ GhostCatが示唆しているように、嘲笑された 'JdbcTemplate'を呼び出すときに引数をキャプチャして、もう一度考えると、より明確でエレガントです – Morfic

答えて

4

あなたが制御しないコードをモックするべきではありません。モックされたクラスがどのように動作するかを知っている(つまり、あなたが定義している)と仮定しているので、あなたのテストがあるコードだけをモックします。

ここでは、jdbcTemplateがどのように機能し、ラムダでそれを呼び出すのが実際にあなたが思っていることをするかどうかはわかりません。

制御しないコードでコードをテストすることは、統合テストのポイントです。私。 CampaignTriggerを実際のデータベース(またはメモリ内のもの)と一緒にテストして、jdbcTemplateを嘲笑することなくテストする必要があります。

+1

私は外部システムの動作を嘲笑することについて同意しますが、ユニットテストでそのシステムとのやりとりが期待通りであるかどうかをチェックするためにmockを使用しても問題ありません。例えば 'verify(mockConnection).prepareStatement(expectedSQL)'です。これにより、しばらくの間だけ実行される統合(通常は遅い)のほかに、コミットする前にいくつかの単体テスト(すばやくすべき)を実行することができます。 – Morfic

+0

可能であれば、最新のコメントに関するご意見もありがとうございます。 – Morfic

+1

@モリック質問は、そのような単体テストの価値についてです。統合テストよりも速く失敗しますか?確かに。しかし、私たちのコントロールを超えたコードが変更された場合、それは失敗するでしょうか?ありそうもない。統合テストを変更するたびに単体テストを変更する必要があるため、各ビルドで数秒を節約するために、時々これを使用するのに数時間を費やす必要があるためです。統合テストは速くなければならず、アプリケーションを開始する必要はありません。遅い場合は、統合テストの上で単体テストを追加することは良い解決策のようには思えません。 –

2

であなたの運を試すことができます。このコールに使用されるオブジェクトは、hereを参照してください。つまり、このようなコードを記述することができます:

ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class); 
verify(mock).doSomething(argument.capture()); 
assertEquals("John", argument.getValue().getName()); 

あなたのメソッドの呼び出しに渡されたオブジェクトへのフルアクセスを与えます!また、最近、mockitoは@Captorアノテーションを導入しています。

編集; @モーフィックのコメントを考えてください:彼が言っていることは絶対に妥当です。

この回答は、特定の問題をどのように解決できるかについての「即時の」ヒントを提供しています。

を超えて:合理的なアプローチは、常に常に「テスト中のユニット」をスライスして、可能な限り小さくすることです!

あなたのクラス/方法は、ただ1つの責任を果たすべきです。可能な最も単純な手段で実装をテストできることを確認します。

質問:「引数キャプチャを使用する必要がありますか、生産コードを改善する必要がありますか」という疑問がある場合は、プロダクションコードを再作成してください。

+0

あなたはこれに参加しており、あなたの答えを引用してきたので、時間があればコメントセクションに参加しますか? :-) – Morfic

+0

@GhostCatあなたの答えをありがとう、私は運がなかった。私はArgumentCaptorを一度も使用していませんが、実際にそれを見てうれしいです。そして確かに、私はコードにいくつかの修正を加えるために必要な他のコメント/回答からのポイントを得ました。次回は、それを念頭に置いていきます。 – Amiko

+0

何かを学ぶのは常に良いことです。そして、それはここの周りのすべての人々にとって本当です;-) – GhostCat

関連する問題