Java单元测试:测试是否调用回调的最简单方法

2022-09-03 17:43:38

我经常使用接受回调的方法,而回调似乎有点难以测试。让我们考虑以下场景,如果有一个方法接受单个方法的回调(为简单起见,我假设测试方法是同步的),则可以编写以下样板文件以确保调用回调方法:

@Test
public void testMethod() {
    final boolean[] passed = {false};
    method(new Callback() {
        @Override
        public void handle(boolean isSuccessful) {
            passed[0] = isSuccessful;
        }
    });
    assertTrue(passed[0]);
}

它看起来像一个代理人。我想知道:有没有一种更优雅的方法来测试这样的代码,使上面的代码看起来更像下面的伪代码?

@Test
public void testMethod() {
    // nothing explicit here, implicit boolean state provided by a test-runner
    method(new Callback() {
        @Override
        public void handle(boolean isSuccessful) {
            if ( isSuccessful ) {
                pass(); // not sure how it should look like:
                        // * an inherited method that sets the state to "true"
                        // * or an object with the pass method
                        // * whatever
                        // but doesn't exit testMethod(), just sets the state
            }
        }
    });
    // nothing explicit here too:
    // test runner might check if the state is changed to true
    // otherwise an AssertionError might be thrown at the end of the method implicitly
}

稍微干净一点。在JUnit,TestNG或任何其他测试框架中是否可能?谢谢!


更新

对不起,我似乎问了一个模糊的问题,这个问题并不符合我想问的问题。我基本上是指任何代码(不一定是回调),如果满足某些条件只是为了将结果状态设置为true,则可以调用这些代码。简单地说,我只想摆脱开头和最后的假设,假设它们分别是某种序幕和尾声,并假设初始状态设置为,因此应该调用它们将状态设置为。无论如何设置为 ,无论从何处。但不幸的是,我使用回调的上下文提出了这个问题,但是这只是一个选项,而不是要求。因此,问题的标题并不反映我真正想问的问题,但在更新之前已经发布了一些答案。boolean[] passedassertTrue(passed[0])falsepass()truepassed[0]true


答案 1

这通常是模拟框架可以为您做的。

Mockito为例:

// imports ommited for brevity
@Test
public void callbackIsCalled()
{
    final CallBack callBack = mock(CallBack.class);
    method(callBack);

    verify(callBack, only()).handle(any());
}

当然,这是验证模式 () 和值匹配器 () 的示例。您可以做更多...only()any()

(存在其他模拟框架,但我个人发现Mockito最容易使用,除了是最强大的框架之一)


答案 2

鉴于这是您在多个地方可能需要的东西,我将创建一个用于测试的命名类:

public class FakeCallback implements Callback {
    private boolean wasSuccessful;
    private boolean handleCalled;

    @Override public void handle(boolean isSuccessful) {
        this.wasSuccessful = isSuccessful;
        handleCalled = true;
    }

    // Getters for fields above
}

然后,您可以使用如下内容:

// Arrange...
FakeCallback callback = new FakeCallback();

// Act...
method(callback);

// Assert
assertTrue(callback.wasHandleCalled());
assertTrue(callback.wasSuccessful());

你绝对可以使用一个模拟框架来代替,但我个人发现,通常创建一个虚假的实现比重复设置模拟更简单。不过,两种方式都可以。


推荐