【问题标题】:Using EasyMock with threads使用带有线程的 EasyMock
【发布时间】:2023-03-24 04:23:01
【问题描述】:

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

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

public class TestStackoverflow {

    @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.TestStackoverflow$EventManager$$EnhancerByCGLIB$$cbb0f0e1.sendEvent(<generated>)
        at at.ac.TestStackoverflow$MyClass$1.run(TestStackoverflow.java:27)
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.58 sec

我该如何测试这种情况?

【问题讨论】:

  • 线程没有捕捉到异常。如果 sendEvent() 抛出一个,线程会像你的主线程一样“中断”,如果一个未处理的异常会被抛出。声明你的 sendEvent() 抛出异常。 public void sendEvent() 抛出异常;这将迫使你在线程的 run() 中尝试捕捉它,你必须在那里处理它。如果测试失败,则强制它在捕获中失败。我帮你了吗?我不确定我是否理解您的问题
  • 是的,但这仅适用于示例。实际上,被测试的类不在单元测试之内,我不会在单元测试之外使用 Assert.fail()。
  • 好吧,我对单元测试了解不多,我是新手。但据我所知,你有两种方法。 a)通过使用 Assert.fail() 强制它失败 b)更改一个标志,如布尔值或其他东西,并在运行代码后对其进行测试。示例布尔失败 = 假。在您的代码末尾执行 if(failed)*强制它失败/产生错误/无论如何使其失败*

标签: java multithreading testng easymock


【解决方案1】:

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

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

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

如果你稍微改变一下,让线程在你的类之外创建,它会更容易测试。

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

public class TestStackoverflow {

    @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();
    }
}

【讨论】:

  • 不要在代码中的其他位置使用 Thread.start,而是查看 java.concurrent.executors 包中的 Executors 内容 - 它使模拟这样的事情更容易
  • 只有当我可以从单元测试中添加 UncaughtExceptionHandler 时,您的解决方案才有效。不幸的是,线程在测试类中创建得非常深,我必须更改多个类才能将 ExceptionHandler 传递给线程。
  • @MarkSitro 考虑改用这种方法... Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler ex)
【解决方案2】:

createStrictMock 所做的是创建一个严格的类模拟,如果在代码中调用的方法不期望在 Testclass 中以特定顺序调用,它将抛出异常。这会导致Unexpected method call 异常(该异常不应首先发生以成功完成测试用例)。

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

这个异常应该解决

祝你好运!

【讨论】:

  • 这解决了异常,但测试仍然没有像它应该的那样失败。有什么方法可以告诉 EasyMock 不抛出异常但仍然注册尝试的方法调用以使测试失败?
  • 但是如果现在没有抛出异常,你想捕捉什么?放一个try-catch就足够了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多