3

我有一个转发方法的类foo

void foo( Concrete c, String s ) { c.bar( s ); }

我想测试是否foo确实转发。对我来说不幸的是,Concrete它是第三方库中的一个类,是一个具体的类型,而不是一个接口。因此我必须ClassImposteriser在 JMock 中使用来模拟Concrete,所以在我的测试用例中,我这样做:

@Test
public final void testFoo() {
   Mockery context = new JUnit4Mockery() {{
      setImposteriser(ClassImposteriser.INSTANCE);
   }};

  final Concrete c = context.mock(Concrete.class);
  final String s = "xxx" ;

  // expectations
  context.checking(new Expectations() {{

     oneOf (c).bar(s); // exception gets thrown from here
  }});


  new ClassUnderTest.foo( c, s );
  context.assertIsSatisfied();

}

不幸的是,Concrete.bar反过来又调用了一个抛出的方法。该方法是最终的,所以我不能覆盖它。此外,即使我注释掉该行new ClassUnderTest.foo( c, s );,当 JMock 设置异常时抛出异常,而不是在foo调用时抛出异常。

那么我如何测试该方法ClassUnderTest.foo确实转发到Concrete.bar

编辑:
是的,酒吧是最终的。

我的解决方案不是通用的,是使用第三方库中的“Tester”类来正确设置Concrete.

4

4 回答 4

5

从问题文本中不清楚 Concrete.bar() 是否是最终的,或者 Concrete.somethingElse() 是否是最终的并从 Concrete.bar() 调用。

如果 Concrete.bar()不是 final,请为 Concrete 创建一个手写存根,如下所示:

public class ConcreteStub extends Concrete
{
    public int numCallsToBar = 0;
    @Override
    public void bar(String s) { numCallsToBar++; }
}

并在您的测试代码中:

ConcreteStub c = new ConcreteStub();
foo(c,"abc");
assertEquals(1,c.numCallsToBar);

如果 Concrete.bar()是 final,它会更复杂,答案取决于 Concrete 的复杂性和您的项目对 Concrete 类的使用。如果您对 Concrete 的使用足够简单,我会考虑将 Concrete 包装在一个接口(适配器模式)中,然后您可以更轻松地模拟出来。

适配器模式解决方案的好处:可能通过在项目使用具体之后命名接口来阐明行为。更容易测试。

适配器模式解决方案的缺点:引入更多类,可能对生产代码几乎没有好处。我不知道 Concrete 做了什么,将 Concrete 包装在界面中可能不切实际。

于 2009-04-12T06:37:40.987 回答
4

有关模拟类以及如何绕过最终限制的信息,请参见http://www.jmock.org/mocking-classes.html 。

于 2009-05-22T20:02:07.620 回答
0

如果一个方法是最终的,那么我们就无能为力了。如果这是一个第三方库,那么我们会考虑将它包装在一个 veneer 层并模拟,然后进行集成测试以针对该库进行测试。还有其他框架可以模拟锁定代码,但我们不支持它,因为我们认为这不是一个好主意。

于 2009-05-21T12:15:20.203 回答
0

使用功能更强大的模拟工具,例如JMockit。然后你的测试可以写成:

@Test
public void testFoo(final Concrete c)
{
  final String s = "xxx";

  new Expectations() {{
    c.bar(s);
  }};

  new ClassUnderTest().foo(c, s);
}

Concrete对于 JMockit,它是一个接口、一个最终类、一个抽象类或其他什么都没有区别。此外,无需使用@RunWith、扩展基本测试类或调用任何方法,例如assertIsSatisfied(); 这一切都是以透明的方式自动完成的。

于 2009-12-31T14:39:56.473 回答