Mock注解的使用方法以及Mock在开源项目中的应用
/最近读源码,都有一个@Mock的注解,这个经常写在单元测试类中,网上一搜,Mock注解的使用文章没有,看来大家都是一看就知道什么意思。
mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。
目前,在Java阵营中主要的Mock测试工具有JMock,MockCreator,Mockrunner,EasyMock,MockMaker等,在微软的.Net阵营中主要是Nmock,.NetMock等。
@RunWith(MockitoJUnitRunner.class)
public class SendMessageProcessorTest {
private SendMessageProcessor sendMessageProcessor;
@Mock //这里Mock一个ChannelHandlerContext类型的变量,下面就直接使用了,那么他们为什么在这里添加在这里呢呢?
private ChannelHandlerContext handlerContext;
@Spy //间谍一个?这个注解也很有讲究
private BrokerController brokerController = new BrokerController(new BrokerConfig(), new NettyServerConfig(), new NettyClientConfig(), new MessageStoreConfig());
@Mock //这里的Mock注解一个messageStore
private MessageStore messageStore;
private String topic = "FooBar";
private String group = "FooBarGroup";
@Before
public void init() {
brokerController.setMessageStore(messageStore);
when(messageStore.now()).thenReturn(System.currentTimeMillis());
Channel mockChannel = mock(Channel.class);
when(mockChannel.remoteAddress()).thenReturn(new InetSocketAddress(1024));
when(handlerContext.channel()).thenReturn(mockChannel);
when(messageStore.lookMessageByOffset(anyLong())).thenReturn(new MessageExt());
sendMessageProcessor = new SendMessageProcessor(brokerController);
}
@Test
public void testProcessRequest() throws RemotingCommandException {
when(messageStore.putMessage(any(MessageExtBrokerInner.class))).thenReturn(new PutMessageResult(PutMessageStatus.PUT_OK, new AppendMessageResult(AppendMessageStatus.PUT_OK)));
assertPutResult(ResponseCode.SUCCESS);
}
}
源码中的解释是这个样子的:
* Mark a field as a mock.标记一个域为一个mock变量
Allows shorthand mock creation.
Minimizes repetitive mock creation code.
Makes the test class more readable.
Makes the verification error easier to read because the field name is used to identify the mock.
使测试代码更易读,是验证错误更容易读
public class ArticleManagerTest extends SampleBaseTestCase {
@Mock
private ArticleCalculator calculator;
@Mock(name = "database")
private ArticleDatabase dbMock;
@Mock(answer = RETURNS_MOCKS)
private UserProvider userProvider;
@Mock(extraInterfaces = {Queue.class, Observer.class})
private articleMonitor;
private ArticleManager manager;
@Before
public void setup() {
manager = new ArticleManager(userProvider, database, calculator, articleMonitor);
}
}
public class SampleBaseTestCase {
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
}
MockitoAnnotations.initMocks(this) method has to be called to initialize annotated objects.
In above example, initMocks() is called in @Before (JUnit4) method of test's base class.
For JUnit3 initMocks() can go to setup() method of a base class.
Instead you can also put initMocks() in your JUnit runner (@RunWith) or use the built-in
{@link MockitoJUnitRunner}.
MockitoAnnotations.initMocks(this)这个代码用来初始化一个注解对象,在上面的例子,initMocks在@Before注解钱调用
对于JUnit3,可以去setup方法,你也可以放在一个JUnit的runner里。
@see Mockito#mock(Class)
@see Spy
@see InjectMocks
@see MockitoAnnotations#initMocks(Object)
@see MockitoJUnitRunner
spy注解用来快速包装一个实例
Allows shorthand wrapping of field instances in an spy object.
Example:
public class Test{
//Instance for spying is created by calling constructor explicitly:
@Spy
Foo spyOnFoo = new Foo("argument");
//Instance for spying is created by mockito via reflection (only default constructors supported):
用反射机制初始化一个实例,使用默认的支持的构造函数
@Spy
Bar spyOnBar;
@Before
public void init(){
MockitoAnnotations.initMocks(this);
}
...
}
Same as doing:
Foo spyOnFoo = Mockito.spy(new Foo("argument"));
Bar spyOnBar = Mockito.spy(new Bar());
A field annotated with @Spy can be initialized explicitly at declaration point.
Alternatively, if you don't provide the instance Mockito will try to find zero argument constructor (even private)
and create an instance for you.
<u>But Mockito cannot instantiate inner classes, local classes, abstract classes and interfaces.</u></strong>
被@spy标注的声明点可以被明确的初始化。如果你不提供实例,Mockito将找到一个零入参的构造函数并且初始化一个实例,
但是Mockto不能初始化内部类,本地类,虚类,接口,例如一下的类便可以被Mockito初始化
For example this class can be instantiated by Mockito :
public class Bar {
private Bar() {}
public Bar(String publicConstructorWithOneArg) {}
}
Important gotcha on spying real objects!
Sometimes it's impossible or impractical to use {@link Mockito#when(Object)} for stubbing spies.
Therefore for spies it is recommended to always use doReturn|Answer|Throw()|CallRealMethod
family of methods for stubbing. Example:
List list = new LinkedList();
List spy = spy(list);
//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");应为spy一个list,内部元素为0,会抛出越界异常,所以要使用when方法,返回一个foo对象
//You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);
Mockito *does not* delegate calls to the passed real instance, instead it actually creates a copy of it.
So if you keep the real instance and interact with it, don't expect the spied to be aware of those interaction
and their effect on real instance state.
The corollary is that when an *unstubbed* method is called *on the spy* but *not on the real instance*,
you won't see any effects on the real instance.
Watch out for final methods.
Mockito doesn't mock final methods so the bottom line is: when you spy on real objects + you try to stub a final method = trouble.
Also you won't be able to verify those method as well.
当心final方法,mockito不mock 任何final方法
One last warning : if you call MockitoAnnotations.initMocks(this) in a
super class constructor then this will not work. It is because fields
in subclass are only instantiated after super class constructor has returned.
It's better to use @Before.
Instead you can also put initMocks() in your JUnit runner (@RunWith) or use the built-in
{@link MockitoJUnitRunner}.
如果你在超级类中调用,这个不会工作,因为子类中的域只有在父类的构造函数返回后才能调用。最好使用@Before注解
不要使用initMocks在你的runner中
Note that the spy won't have any annotations of the spied type, because CGLIB won't rewrite them.
It may troublesome for code that rely on the spy to have these annotations.
注意spy没有任何spied化类型的注解,因为cglib不重写他们,他可能引起一些麻烦
@see Mockito#spy(Object)
@see Mock
@see InjectMocks
@see MockitoAnnotations#initMocks(Object)
@see MockitoJUnitRunner
@since 1.8.3
看不懂