繁体   English   中英

如何使用 JUnit 或 Mockito 测试匿名方法?

[英]How to test anonymous methods with JUnit or Mockito?

我有简单的类,但有匿名代码块。 我需要用测试来覆盖这个类。

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) ); -> 在 com.nxsystems.dw.publisher.handler.CleanerTaskTest.successfulScenario(CleanerTaskTest.java:52) 实际上,与此模拟的交互为零。

在 com.nxsystems.dw.publisher.handler.CleanerTaskTest.successfulScenario(CleanerTaskTest.java:52) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) .reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) at 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 .runners.statements.RunBefores.evaluate(RunBefores.java:28) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) 在 org.ju nit.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) 在 org.junit.runners.ParentRunner.run(ParentRunner.java:236) 在 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 .main(JUnitStarter.java:62)

此外,当这个 tets 启动时,调试器不会进入匿名块。 那么如何让 Mockito 进入匿名块呢?

为什么它不起作用

那么你的问题是你的测试中的TransactionTemplate是一个模拟。 因此它具有与TransactionTemplate相同的接口,但它不知道如何表现。 你负责它的实现——这就是模拟的重点。 您在代码中显式调用template.execute() ,这就是您的第一次验证通过的原因。 但是那个execute()不是来自 Spring 的那个(或者更准确地说,你的测试中的template不是 Spring 的TransactionTemplate一个实例,它只是它的一个模拟) - 它是,好吧,我们可以说它是“空的”,因为它被调用在模拟上,你没有告诉模拟调用execute()应该如何表现。

我将如何修复它

在这种情况下,我真的不鼓励您进行此类单元测试,因为您正在此处测试实现 你应该考什么,至少根据我来说,是意味着一定条件下的功能事情发生那么应该发生了一些成绩。 这需要将其更改为集成测试(使用 DBUnit 或其他任何东西)并断言您是否真的删除了您应该删除的内容。 我的意思是你真正关心的是什么 - 知道一些方法被调用了或者你希望的事情真的发生了?

你怎么能,但恕我直言不应该,修复它。

但是如果你真的想测试那段匿名代码,那么我会简单地将它(整个匿名类)提取到一个单独的类中,并为这个新类编写一个单元测试,更准确地说是为它的doInTransaction()方法。 在这种情况下,您将使用new创建它,在其中设置一个模拟DataWarehouseMessageDao并简单地执行verify()

你不应该改变你的代码,这是对的。 在您的单元测试中,您应该使用ArgumentCaptor来捕获传递的参数,而不是使用 isA 检查,验证该实例是 TransactionCallback 的类型并在其上调用 doInTransaction 方法。 因此,您将能够验证是否使用预期参数调用了 dao(建议您可以使用eq匹配器来验证确切值)。

确实,在这个测试中你一次要测试两件事,但这只是因为你的实现,我并不是说它是错的。 使用某些业务逻辑创建新实例总是会增加代码中的耦合,但这并不意味着我们不应该使用语言能力来做到这一点。

这是处理 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)));

此解决方案将在此虚假交易中执行代码,您可以在之后验证答案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM