【问题标题】:Delay EasyMock verification延迟 EasyMock 验证
【发布时间】:2010-01-05 09:03:46
【问题描述】:

我正在使用 EasyMock 为 Java 中的 JUnit 测试创建模拟对象。我创建了一个模拟对象并将其传递给另一个线程,它期望调用方法。在另一个线程中,调用包含在try/catch(Throwable) 块中,因此当模拟上发生意外调用并因此抛出AssertionError 时,该错误被catch 块捕获并处理。因此,即使发生了意外调用,测试也通过了。

为了使测试按预期失败,我想在最后延迟测试运行线程中对EasyMock.verify(mock) 调用的所有调用验证。这可能吗?如何实现?

【问题讨论】:

    标签: java unit-testing junit mocking easymock


    【解决方案1】:

    我不确定如何使用 EasyMock 执行此操作,但 Mockito 可以实现此行为,因为可以在测试结束时指定验证断言。

    【讨论】:

      【解决方案2】:

      我猜正确的解决方案是停止捕获Throwable。这样做会捕获您所发现的所有Errors,这可能非常危险……您是否绝对100%肯定需要捕获Throwable?为什么?

      (如果事实证明你这样做了,你可以专门抓住 AssertionError 并重新抛出它。但这很难看!)

      【讨论】:

      • 我们捕获 Throwable(丑陋,我同意),因此我们可以拦截(例如)内存不足错误并进行一些日志记录并从那时起干净地重新启动应用程序。如果没有其他解决方案,我可能会求助于捕获 AssertionError。
      • @Steven - 捕获 AssertionError 并重新抛出它几乎肯定没有无条件的 catch (Throwable t) 块那么难看。 :-)
      【解决方案3】:

      尝试使用漂亮的模拟:

      http://easymock.org/EasyMock2_5_2_Documentation.html

      “漂亮的模拟 在 createMock() 返回的 Mock 对象上,所有方法的默认行为是为所有意外的方法调用抛出 AssertionError。如果您想要一个默认情况下允许所有方法调用并返回适当的空值(0、null 或 false)的“漂亮”模拟对象,请改用 createNiceMock()。 "

      意外调用将返回默认值而不是抛出 AssertionError,但您仍然可以使用 verify() 方法验证它们(在这种情况下将抛出 AssertionErrors)

      【讨论】:

      • 我也同意 deb 的建议:Mockito 确实提供了一种更简单的模拟机制(即不必担心 nice/strick 模拟),并且您可以只验证您实际需要的行为。
      • +1:当您当前的库提供您需要的功能时,不值得切换库!
      • -1:一个不错的模拟将接受任何方法调用,因此不允许测试是否发生意外调用(仅当预期调用没有发生时)。
      • 你是对的,没这么看;我尝试过尝试,但找不到更好的解决方案。也许您可以尝试降低测试的粒度,例如测试更具体的方法。
      【解决方案4】:

      正如@deterb 建议的那样,使用 Mockito 是可能的,但您必须知道方法名称,否则您必须为每个方法设置期望值。这是一个例子:

      模拟界面:

      public interface MyInterface {
      
          void allowedMethod();
      
          void disallowedMethod();
      }
      

      捕获AssertionError的用户类:

      public class UserClass {
      
          public UserClass() {
          }
      
          public static void throwableCatcher(final MyInterface myInterface) {
              try {
                  myInterface.allowedMethod();
                  myInterface.disallowedMethod();
              } catch (final Throwable t) {
                  System.out.println("Catched throwable: " + t.getMessage());
              }
          }
      }
      

      还有 Mockito 测试:

      @Test
      public void testMockito() throws Exception {
          final MyInterface myInterface = mock(MyInterface.class);
      
          UserClass.throwableCatcher(myInterface);
      
          verify(myInterface, never()).disallowedMethod(); // fails here
      }
      

      EasyMock 也可以做到这一点,但需要一些工作:

      @Test
      public void testEasyMock() throws Exception {
          final AtomicBoolean called = new AtomicBoolean();
          final MyInterface myInterface = createMock(MyInterface.class);
          myInterface.allowedMethod();
      
          myInterface.disallowedMethod();
          final IAnswer<? extends Object> answer = new IAnswer<Object>() {
      
              @Override
              public Object answer() throws Throwable {
                  System.out.println("answer");
                  called.set(true);
                  throw new AssertionError("should not call");
              }
      
          };
          expectLastCall().andAnswer(answer).anyTimes();
      
          replay(myInterface);
      
          UserClass.throwableCatcher(myInterface);
      
          verify(myInterface);
          assertFalse("called", called.get()); // fails here
      }
      

      不幸的是,您还必须知道这里的方法名称,并且您必须定义像 myInterface.disallowedMethod()expectLastCall().andAnswer(answer).anyTimes() 这样的期望。

      另一种可能性是使用Proxy class(使用自定义InvocationHandler)创建代理并将其用作模拟对象。它肯定需要更多的工作,但它可能是最可定制的解决方案。

      最后不要忘记,也可以创建自定义实现,无论是否委托给 EasyMock 模拟对象。这是一个有委托的:

      public class MockedMyInterface implements MyInterface {
      
          private final MyInterface delegate;
      
          private final AtomicBoolean called = new AtomicBoolean();
      
          public MockedMyInterface(final MyInterface delegate) {
              this.delegate = delegate;
          }
      
          @Override
          public void allowedMethod() {
              delegate.allowedMethod();
          }
      
          @Override
          public void disallowedMethod() {
              called.set(true);
              throw new AssertionError("should not call");
          }
      
          public boolean isCalled() {
              return called.get();
          }
      
      }
      

      以及对它的测试:

      @Test
      public void testEasyMockWithCustomClass() throws Exception {
          final MyInterface myInterface = createMock(MyInterface.class);
          myInterface.allowedMethod();
      
          final MockedMyInterface mockedMyInterface = 
              new MockedMyInterface(myInterface);
      
          replay(myInterface);
      
          UserClass.throwableCatcher(mockedMyInterface);
      
          verify(myInterface);
          assertFalse("called", mockedMyInterface.isCalled()); // fails here
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-11
        • 2021-08-25
        • 2023-04-06
        • 2020-03-07
        • 2016-09-19
        相关资源
        最近更新 更多