下文为转载,但原文有问题,错误的地方特地标红了
Introduction
Code in which new objects are created can be difficult to test. There are a number of patterns for doing this; two of them are discussed here. Both of these patterns may require rearranging your code a little, to make it more testable. Writing code that's easily testable is a good thing to be doing, regardless of which mocking framework you end up using.
Details
Pattern 1 involves factoring uses of new into one-line methods, then using a Mockito spy. This is the simpler of the two patterns. Pattern 2 involves factoring uses of new into a separate class and injecting it. It's a little more work, but it can be more powerful. The new calls that you want to factor out (using either pattern) are those where an object is created, that you are likely to want to mock. It is recommended that you use one or other of these patterns, whenever you find yourself writing new, on a class that's in your code base (as opposed to a JDK class or a class from a third party library).
Pattern 1 - using one-line methods for object creation
To use pattern 1 (testing a class called MyClass), you would replace a call like
Foo foo = new Foo( a, b, c );
with
Foo foo = makeFoo( a, b, c );
and write a one-line method
Foo makeFoo( A a, B b, C c ){ return new Foo( a, b, c ); }
It's important that you don't include any logic in the method; just the one line that creates the object. The reason for this is that the method itself is never going to be unit tested.
When you come to test the class, the object that you test will actually be a Mockito spy, with this method overridden, to return a mock. What you're testing is therefore not the class itself, but a very slightly modified version of it.
Your test class might contain members like
@Mock private Foo mockFoo = mock(Foo.class); private MyClass toTest = spy( new MyClass());
Lastly, inside your test method you mock out the call to makeFoo with a line like
doReturn( mockFoo ) .when( toTest ) .makeFoo( any( A.class ), any( B.class ), any( C.class ));
You can use matchers that are more specific than any() if you want to check the arguments that are passed to the constructor.
Pattern 2 - the factory helper pattern
One case where this pattern won't work is if MyClass is final. Most of the Mockito framework doesn't play particularly well with final classes; and this includes the use of spy(). Another case is where MyClass uses getClass() somewhere, and requires the resulting value to be MyClass. This won't work, because the class of a spy is actually a Mockito-generated subclass of the original class.
In either of these cases, you'll need the slightly more robust factory helper pattern, as follows.
public class MyClass{ static class FactoryHelper{ Foo makeFoo( A a, B b, C c ){ return new Foo( a, b, c ); } } //... private FactoryHelper helper; public MyClass( X x, Y y ){ this( x, y, new FactoryHelper()); } MyClass( X x, Y, y, FactoryHelper helper ){ //... this.helper = helper; } //... Foo foo = helper.makeFoo( a, b, c ); }
So, you have a special constructor, just for testing, that has an additional argument. This is used from your test class, when creating the object that you're going to test. In your test class, you mock the FactoryHelper class, as well as the object that you want to create.
@Mock private MyClass.FactoryHelper mockFactoryHelper; @Mock private Foo mockFoo; private MyClass toTest;
and you can use it like this
toTest = new MyClass( x, y, mockFactoryHelper ); when( mockFactoryHelper.makeFoo( any( A.class ), any( B.class ), any( C.class ))) .thenReturn( mockFoo );
As with pattern 1, you can use matchers that are more specific than any() if you need to.