9

私は単純なクラスを持っていますが、コードの匿名ブロックがあります。このクラスをテストでカバーする必要があります。

public class CleanerTask {

    private final Logger log = LoggerFactory.getLogger(getClass());
    DataWarehouseMessageDao dwMessageDao;
    int cleanerDelay = 0;
    TransactionTemplate template;

    public CleanerTask(DataWarehouseMessageDao dwMessageDao, int cleanerDelay, TransactionTemplate template) {
        this.dwMessageDao = dwMessageDao;
        this.cleanerDelay = cleanerDelay;
        this.template = template;
    }

    public void clean() {
        log.info("Cleaner started");
        final Date olderThan = new Date();
        olderThan.setDate(olderThan.getDate() + cleanerDelay);
        template.execute(new TransactionCallback<Date>() {
            @Override
            public Date doInTransaction(TransactionStatus transactionStatus) {
                dwMessageDao.deleteAllByStatusAndDate(DataWarehouseMessageStatus.SAVED.getValue(), olderThan);
                return olderThan;
            }
        });
    }
}

そしてテスト:

@RunWith(MockitoJUnitRunner.class)
public class CleanerTaskTest {

    final static int CLEANER_DELAY = 5;

    @Mock
    DataWarehouseMessageDao dao;

    @Mock
    TransactionTemplate template;

    CleanerTask cleanerTask;

    @Before
    public void setUp() throws Exception {
        cleanerTask = new CleanerTask(dao, CLEANER_DELAY, template);
    }

    @Test
    public void successfulScenario() {
        try {
            cleanerTask.clean();
            verify(template, times(1)).execute(isA(TransactionCallback.class));
            //that verify was not triggered    
            //verify(dao, times(1)).deleteAllByStatusAndDate(anyInt(), isA(Date.class));
        } catch (Exception e) {
            e.printStackTrace();
            fail("No exceptions must occur");
        }
    }
}

コメント行が機能しません。ログ: 必要ですが呼び出されていません: dao.deleteAllByStatusAndDate( , isA(java.util.Date) ); -> at com.nxsystems.dw.publisher.handler.CleanerTaskTest.successfulScenario(CleanerTaskTest.java:52) 実際、このモックとのやり取りはありませんでした。

com.nxsystems.dw.publisher.handler.CleanerTaskTest.successfulScenario(CleanerTaskTest.java:52) で sun.reflect.NativeMethodAccessorImpl.invoke0(ネイティブ メソッド) で sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) で.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable. java:15) org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) で org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) で org.junit.internal org.junit.runners.BlockJUnit4ClassRunner の .runners.statements.RunBefores.evaluate(RunBefores.java:28)。runChild(BlockJUnit4ClassRunner.java:76) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners. ParentRunner$1.schedule(ParentRunner.java:52) org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) org.junit .runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37) org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62) で org.junit.runner.JUnitCore.run(JUnitCore.java:157) で com.intellij.rt.execution.junit.JUnitStarter.メイン(JUnitStarter.java:62)

また、この tets が開始されると、デバッガーは無名ブロック内に入りません。では、Mockito を匿名ブロック内に移動させる方法は?

4

3 に答える 3

14

うまくいかない理由

ここでの問題はTransactionTemplate、テストがモックであることです。そのため、 と同じインターフェースを持っていますが、TransactionTemplateどのように動作するかはわかりません。あなたはその実装を担当しています - それがモックの要点です。コードで明示的に呼び出しtemplate.execute()ているため、最初の検証が成功します。しかし、それexecute()はSpringのものではありません(または、より正確にtemplateは、テストではSpringのインスタンスではなく、TransactionTemplate単なるモックです)-それは、モックで呼び出され、あなたが呼び出しexecute()がどのように振る舞うべきかをモックに伝えませんでした。

どうすれば修正できますか

このような場合、ここで実装をテストしているため、そのような単体テストをやめることを本当にお勧めします。少なくとも私によると、テストする必要があるのは、特定の条件が与えられた場合に機能を意味することです。何かが発生すると、何らかの結果が発生するはずです。これには、これを統合テストに変更し(DBUnitなどを使用して)、削除するはずのものを実際に削除したかどうかをアサートする必要があります。つまり、いくつかのメソッドが呼び出されたことや、望んでいたことが実際に起こったことを知っているということです。

どうすればできますが、私見ではすべきではありませんが、修正してください。

しかし、本当にそのコードの匿名部分をテストしたい場合は、それ (匿名クラス全体) を別のクラスに抽出し、その新しいクラス、より正確にはそのdoInTransaction()メソッドの単体テストを記述します。その場合、 を使用して作成し、その中newにモックを設定DataWarehouseMessageDaoして、単にverify().

于 2012-08-15T00:48:59.617 に答える
2

コードを変更するべきではありません。単体テストでは、isA チェックの代わりに、ArgumentCaptorを使用して渡されたパラメーターをキャプチャし、インスタンスが TransactionCallback のタイプであることを検証し、doInTransaction メソッドを呼び出す必要があります。したがって、必要なパラメーターで dao が呼び出されたことを確認できます (正確な値を確認するためにeqマッチャーを使用できることをお勧めします)。

このテストでは一度に 2 つのことをテストするのは事実ですが、それは単に実装のせいであり、それが間違っていると言っているわけではありません。いくつかのビジネス ロジックを使用して新しいインスタンスを作成すると、常にコード内の結合が増加しますが、それを行うために言語機能を使用してはならないという意味ではありません。

于 2012-08-20T16:23:41.103 に答える
0

transactionTemplate (Java 8+) を処理する別の方法を次に示します。

    TransactionTemplate transactionTemplate = mock(TransactionTemplate.class);
    when(transactionTemplate.execute(any(TransactionCallback.class)))
       .then(invocation -> ((TransactionCallback<YourReturnClass>) invocation.getArgument(0))
       .doInTransaction(any(TransactionStatus.class)));

このソリューションは、この偽のトランザクション内でコードを実行し、後で回答を確認できます。

于 2020-04-13T18:12:28.273 に答える