简体   繁体   English

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

[英]Testing a concrete third-party class with JMock

I have class with a forwarding method foo : 我有一个转发方法foo

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

I wish to test whether foo does, in fact, forward. 我想测试foo是否确实是前进的。 Unfortunately for me, Concrete is a class in a third-party library, and is a concrete type, not an interface. 不幸的是, Concrete是第三方库中的一个类,它是一个具体的类型,而不是一个接口。 Thus I must use ClassImposteriser in JMock to mock Concrete , so in my test case, I do this: 因此,我必须在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();

} }

Unfortunately, Concrete.bar in turn calls a method that throws. 不幸的是, Concrete.bar依次调用抛出的方法。 That method is final, so I can't override it. 那个方法是最终的,所以我无法覆盖它。 Further, even if I comment out the line new ClassUnderTest.foo( c, s ); 此外,即使我注释掉了new ClassUnderTest.foo( c, s ); , the exception is thrown when JMock sets up exceptions, not when foo is called. ,当JMock设置异常时抛出异常,而不是在调用foo时抛出异常。

So how can I test that method ClassUnderTest.foo does forward to Concrete.bar ? 那么我如何测试ClassUnderTest.foo转发到Concrete.bar呢?

Edit: 编辑:
Yes, bar is final. 是的,酒吧是最终的。

My solution, which is not a general one, was to use a "Tester" class in the third-party library to correctly set up Concrete . 我的解决方案不是一般的,是在第三方库中使用“Tester”类来正确设置Concrete

It's not clear from the question text if Concrete.bar() is final or if Concrete.somethingElse() is final and called from Concrete.bar(). 如果Concrete.bar()是final或者Concrete.somethingElse()是final并且是从Concrete.bar()调用的,那么从问题文本中就不清楚了。

If Concrete.bar() is not final , create a hand-written stub for Concrete like this: 如果Concrete.bar() 不是final ,那么为Concrete创建一个手写的存根,如下所示:

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

and in your test code: 并在您的测试代码中:

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

If Concrete.bar() is final , it is more complicated and the answer depends on the complexity of Concrete and your project's use of the Concrete class. 如果Concrete.bar() 是最终的 ,它会更复杂,答案取决于Concrete的复杂性和您的项目对Concrete类的使用。 If your use of Concrete is simple enough, I would consider wrapping Concrete in an interface ( Adapter Pattern ) that you can then mock out easier. 如果你对Concrete的使用很简单,我会考虑在一个接口( 适配器模式 )中包装Concrete,然后你可以更容易地模拟出来。

Benefits to the Adapter Pattern solution: Possibly clarify behavior by naming interface after your project's use of Concrete. 适配器模式解决方案的好处:在项目使用Concrete之后,通过命名接口可能会澄清行为。 Easier to test. 更容易测试。

Drawbacks to the Adapter Pattern solution: Introduces more classes with possibly little benefit to production code. 适配器模式解决方案的缺点:引入更多类,对生产代码可能没什么好处。 I don't know what Concrete does and it may not be practical to wrap Concrete in an interface. 我不知道Concrete做了什么,在接口中包装Concrete可能不切实际。

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

Use a more capable mocking tool, such as JMockit . 使用功能更强大的模拟工具,例如JMockit Your test could then be written as: 您的测试可以写成:

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

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

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

For JMockit, it makes no difference if Concrete is an interface, a final class, an abstract class, or whatever. 对于JMockit,如果Concrete是一个接口,一个final类,一个抽象类或者其他什么,它没有任何区别。 Also, there is no need to use @RunWith , extend a base test class, or call any method like assertIsSatisfied() ; 此外,不需要使用@RunWith ,扩展基础测试类,或调用任何方法,如assertIsSatisfied() ; it's all done automatically, in a transparent way. 这一切都是以透明的方式自动完成的。

If a method is final then we can't do much about it. 如果一种方法是最终的,那么我们就不能做太多。 If this is a third-party library, then we would consider wrapping it in a veneer layer and mocking that , then doing integration tests to test against the library. 如果这是第三方库,那么我们会考虑将其包装在胶合层中并模拟 ,然后进行集成测试以对库进行测试。 There are other frameworks that will mock locked-down code, but we don't support it because we don't think it's a great idea. 还有其他框架会模拟锁定代码,但我们不支持它,因为我们认为这不是一个好主意。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM