cd ../

Mockito六个简单示例(翻译)

原文地址:Mockito in six easy examples

原文作者:Gojko Adzic

Mockito是个奇妙的Java模拟对象(mock)类库。与其他Java或是.NET模拟对象类库相比,我被它简单、易用的特性所吸引。接下来我会列举六个十分简单的例子来帮助你了解Mockit。

首先,前往主页下载mockito,把org.mockito.Mockitoimport(或者使用static import,下文都使用这种方式)进你的代码中,然后开始感受它的奇妙之处吧。

使用mock(class)来创建一个桩(stub)或者一个模拟对象(译者注:关于桩与模拟的区别可以参考Martin Fowler的文章Mocks Aren’t Stubs)。然后,使用when(mock).thenReturn(value)来指定这个桩返回的值。如果你指定了不止一个值,这个桩将会顺序返回这些值,直到最后一个将会不变(所以指定一次返回值,这个方法的返回就不会变了)。例如,

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import java.util.Iterator;
import org.junit.Test;
....
    @Test
    public void iterator_will_return_hello_world(){
        //arrange
        Iterator i=mock(Iterator.class);
        when(i.next()).thenReturn("Hello").thenReturn("World");
        //act
        String result=i.next()+" "+i.next();
        //assert
        assertEquals("Hello World", result);
    }

在这个例子中,我们创建了一个模拟的迭代器,第一次调用next()时返回"Hello",第二次返回"World"。接着我们做一个普通的断言(Assertion)。

桩当然也可以根据对方法传递的不同参数返回不同的值。例如,

    @Test
    public void with_arguments(){
        Comparable c=mock(Comparable.class);
        when(c.compareTo("Test")).thenReturn(1);
        assertEquals(1,c.compareTo("Test"));
    }

在上面的例子里,我们创建了一个Comparable的桩对象,当它和一个特定的值进行比较时(这里是"Test")返回1。如果你并不能预测到这个方法将会传递的参数,或者你更不关心参数是什么,可以使用anyInt()(当然还有其他类型的替代)。举个例子,

    @Test
    public void with_unspecified_arguments(){
        Comparable c=mock(Comparable.class);
        when(c.compareTo(anyInt())).thenReturn(-1);
        assertEquals(-1,c.compareTo(5));
    }

这个Comparable的桩对象,不管我们传递任何参数,这个方法总是返回-1。如果你的方法是void型的,这个就有点讨巧了,因为你没法在when中传递这个方法。可以把之前的方式替换成doReturn(result).when(mock_object).void_method_call();。除了可以指定返回,你还可以使用.thenThrow()或者doThrow()。例如,

    @Test(expected=IOException.class)
    public void OutputStreamWriter_rethrows_an_exception_from_OutputStream() 
            throws IOException{
        OutputStream mock=mock(OutputStream.class);
        OutputStreamWriter osw=new OutputStreamWriter(mock);
        doThrow(new IOException()).when(mock).close();
        osw.close();
    }

这个例子中,当我们调用OutputStreamclose方法时,会抛出IOException。我们简单地可以验证,OutputStreamWriter从包装的输出流(译者注,这边的包装对象使用到的是装饰器模式)中重新抛出了这个异常。为了验证相关对象实际的调用(特别被用于模拟对象),我们可以使用verify(mock_object).method.call。例如,

    @Test
    public void OutputStreamWriter_Closes_OutputStream_on_Close()
            throws IOException{
        OutputStream mock=mock(OutputStream.class);
        OutputStreamWriter osw=new OutputStreamWriter(mock);
        osw.close();
        verify(mock).close();
    }

这个例子将会验证当OutputStreamWriter被关闭时,是否同时关闭了包装的输出流。就和之前的那个例子一样,通过使用类似anyInt()的匹配器来匹配方法中的参数。这边要注意,你不能把文本参数(译者注:直接传递具体的参数值)和匹配器参数混合使用,所以如果你使用了多个参数,同时包含了文本参数或是匹配参数。使用eq(value)匹配器把文本参数转换为匹配参数来作比较。Mockito内建了许多匹配器,但是有时候你需要灵活使用。例如,OutputStreamWriter会对输出设置缓冲,直到倾倒缓冲的时候才会使用到包装的输出流对象,但是我们不知道究竟这个缓冲有多大,所以我们不能做等价的匹配。我们需要使用我们自己的匹配:

@Test
public void OutputStreamWriter_Buffers_And_Forwards_To_OutputStream() 
    throws IOException{     
    OutputStream mock=mock(OutputStream.class);
    OutputStreamWriter osw=new OutputStreamWriter(mock);
    osw.write('a');
    osw.flush();
    // can't do this as we don't know how long the array is going to be
    // verify(mock).write(new byte[]{'a'},0,1);

    BaseMatcher arrayStartingWithA=new BaseMatcher(){
        @Override
        public void describeTo(Description description) {
            // nothing
        }
        // check that first character is A
        @Override
        public boolean matches(Object item) {
            byte[] actual=(byte[]) item;
            return actual[0]=='a';
        }
    };
    // check that first character of the array is A, and that the other two arguments are 0 and 1
    verify(mock).write(argThat(arrayStartingWithA), eq(0),eq(1));   
}

就说到这里,这些已经包含了你所需要了解的基础知识。现在开始深入学习,重构你那些丑陋的easymock代码吧。