使用JMock测试具体的第三方类

时间:2009-04-12 02:19:35

标签: java unit-testing jmock

我的课程使用转发方法foo

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

我希望测试foo是否确实是前进的。对我来说不幸的是,Concrete是第三方库中的一个类,它是一个具体的类型,而不是一个接口。因此,我必须在JMock中使用ClassImposteriser来模拟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 个答案:

答案 0 :(得分:5)

从问题文本中不清楚Concrete.bar()是否为final或者Concrete.somethingElse()是final还是从Concrete.bar()调用。

如果Concrete.bar()不是最终,请像这样为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()是最终的,则它更复杂,答案取决于Concrete的复杂性以及您的项目对Concrete类的使用。如果你对Concrete的使用很简单,我会考虑在一个接口(Adapter Pattern)中包装Concrete,然后你可以更容易地模拟出来。

适配器模式解决方案的好处:在项目使用Concrete之后,通过命名接口可能会澄清行为。更容易测试。

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

答案 1 :(得分:4)

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

答案 2 :(得分:0)

如果某种方法是最终的,那么我们就无法做很多事情。如果这是第三方库,那么我们会考虑将其包装在胶合代码层中并模拟 ,然后进行集成测试以对库进行测试。还有其他框架会模拟锁定代码,但我们不支持它,因为我们认为这不是一个好主意。

答案 3 :(得分:0)

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

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

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

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

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