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

tpd*_*pdi 3 java unit-testing jmock

我有一个转发方法的类foo:

void foo( Concrete c, String s ) { c.bar( s ); }
Run Code Online (Sandbox Code Playgroud)

我想测试一下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();
Run Code Online (Sandbox Code Playgroud)

}

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

所以,我怎么能测试方法ClassUnderTest.foo不期待Concrete.bar

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

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

Ale*_*x B 5

如果Concrete.bar()是final或者Concrete.somethingElse()是final并且是从Concrete.bar()调用的,那么从问题文本中就不清楚了.

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

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

并在您的测试代码中:

ConcreteStub c = new ConcreteStub();
foo(c,"abc");
assertEquals(1,c.numCallsToBar);
Run Code Online (Sandbox Code Playgroud)

如果Concrete.bar()是最终的,那么它会更复杂,答案取决于Concrete的复杂性以及您的项目对Concrete类的使用.如果你对Concrete的使用很简单,我会考虑在一个接口(适配器模式)中包装Concrete,然后你可以更容易地模拟出来.

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

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