将 EasyMock 与线程一起使用

Using EasyMock with threads

这看起来很简单,但我不知道该怎么做。我们有一个状态机,当触发转换时,事件通过 EventManager class 发送。这是在新线程中启动的,因为我们不想在分发事件时阻塞当前线程。

假设我忘记在模拟时期待方法调用。测试现在应该失败了。但这不是因为线程捕获了异常。我也不想传播期望,因为状态机在遇到异常时停止。

public class TestWhosebug {

    @Test
    public void test(){
        EventManager eventManager = EasyMock.createStrictMock(EventManager.class);
        EasyMock.replay(eventManager);
        MyClass myClass = new MyClass();
        myClass.executeStuff(eventManager);
        EasyMock.verify(eventManager);
    }

    private class MyClass{
        public void executeStuff(final EventManager eventManager){
            new Thread() {
                public void run() {
                    eventManager.sendEvent();
                }  
            }.start();
        }
    }

    interface EventManager{
        public void sendEvent();
    }
}

eventManager.sendEvent() 抛出 java.lang.AssertionError,但单元测试没有失败。

Exception in thread "Thread-0" java.lang.AssertionError:
  Unexpected method call EventManager.sendEvent():
        at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:44)
        at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:94)
        at org.easymock.internal.ClassProxyFactory$MockMethodInterceptor.intercept(ClassProxyFactory.java:97)
        at at.ac.TestWhosebug$EventManager$$EnhancerByCGLIB$$cbb0f0e1.sendEvent(<generated>)
        at at.ac.TestWhosebug$MyClass.run(TestWhosebug.java:27)
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.58 sec

我该如何测试这种情况?

即使异常确实传播回您的测试,它仍然可能无法正常工作,因为看起来可能存在竞争条件。

例如,在您的测试中,"executeStuff" 会启动线程,但它实际上可能没有机会立即 运行。

如果主线程继续,它可能会在其他线程有机会 运行 之前到达 "verify" 语句。

如果您稍作更改,以便在 class 之外创建线程,测试起来会更容易。

然后您可以使用 UncaughtExceptionHandler 来解决您原来的问题,例如...

public class TestWhosebug {

    @Test
    public void test(){
        TestExceptionHandler exceptionHandler = new TestExceptionHandler();
        EventManager eventManager = EasyMock.createStrictMock(EventManager.class);
        EasyMock.replay(eventManager);

        // Create a MyClass - pass in the eventManager
        MyClass myClass = new MyClass(eventManager);

        // Create thread
        Thread thread = new Thread(myClass);
        thread.setUncaughtExceptionHandler(exceptionHandler);

        // Run your code
        thread.start();

        // WAIT FOR THREAD TO FINISH
        thread.join();

        // Check no exceptions were thrown
        exceptionHandler.verifyNoExceptions();
    }

    private class TestExceptionHandler implements UncaughtExceptionHandler {

        private Throwable e;

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            this.e = e;
        }

        public void verifyNoExceptions() {
            if(e != null) {
                throw new AssertionError("BOOM! Exception caught", e);
            }
        }

    }

    private class MyClass implements Runnable {
        private EventManager eventManager;

        public MyClass(EventManager eventManager) {
            this.eventManager = eventManager;
        }

        public void run(){
            eventManager.sendEvent();
        }
    }

    interface EventManager{
        public void sendEvent();
    }
}

createStrictMock 的作用是创建一个严格的 Class 模拟,如果在代码中调用的方法在 Testclass 中的特定顺序不符合预期,它将抛出异常。这导致 Unexpected method call 异常(为了成功完成测试用例,不应首先发生异常)。

你需要创建一个漂亮的模拟,即 Easymock.createNiceMock(EventManager.class);

应该解决这个异常

祝你好运!