Mockテスト

Mockito + Powermock ≒ JMockit

JMockit

http://code.google.com/p/jmockit/

基本的な使い方

public class Target {

public String method() {

XxxService service = new XxxService();

return service.doExecute();

}

}

import mockit.Mocked;

import mockit.Expectations;

import mockit.NonStrictExpectations;

@Mocked

private XxxService xxxService;

@Test

public void test(){

new Expectations(xxxService) {

{

xxxService.doExecute();

returns("xxx");

}

};

new NonStrictExpectations() {

{

Account account = new Account();

account.setBalance(1000);

xxxService.getAccount("xxx");

returns(account);

}

};

String result = new Target().method();

...

}

privateメソッドのMock化

@Mocked("method")

private XxxService xxxService;

@Test

public void test(){

new NonStrictExpectations() {

{

invoke(xxxService,

"method",

withAny("xxx"),

withAny(new Integer(10)));

result = "success";

}

};

xxxService.doExecute();

...

}

PowerMock

http://code.google.com/p/powermock/

※導入前に設計を見直すべき

<!-- JUnit -->

<dependency>

<groupId>org.powermock</groupId>

<artifactId>powermock-module-junit4</artifactId>

<version>${powermock.version}</version>

<scope>test</scope>

</dependency>

<!-- TestNG -->

<dependency>

<groupId>org.powermock</groupId>

<artifactId>powermock-module-testng</artifactId>

<version>${powermock.version}</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.powermock</groupId>

<artifactId>powermock-api-mockito</artifactId>

<version>${powermock.version}</version>

<scope>test</scope>

</dependency>

import org.powermock.core.classloader.annotations.PrepareForTest;

import org.powermock.modules.testng.PowerMockTestCase;

import org.powermock.api.mockito.PowerMockito;

// JUnit

//@RunWith(PowerMockRunner.class)

//@PrepareForTest({Target.class})

//public class MockTest {

// TestNG

@PrepareForTest({Target.class})

public class MockTest extends PowerMockTestCase {

@Mock

private Local local;

// NOTE: Custom arg × (DataProviderとPowerMockTestCase間の問題)

@Test(dataProvider = "...")

public void shouldXxx(Object arg) {

PowerMockito.whenNew(Local.class)

.withNoArguments().thenReturn(local);

doReturn((Custom)arg).when(local).doSomething();

...

}

}

class Target {

public void method() {

...

Local local = new Local();

...

Custom c = local.doSomething();

...

}

}

※注意点

・JaCoCo(Sonarに組み込む)、EclEmmaを利用する場合、

@PrepareForTestをつけるクラスのCoverageは0%になる

・Coberturaを利用する場合、OK

<plugin>

<groupId>org.codehaus.mojo</groupId>

<artifactId>cobertura-maven-plugin</artifactId>

<version>2.6</version>

</plugin>

Mockito(続く)

Mockito cannot mock:

・final classes

・enums

・final methods

・static methods

・private methods

・hashCode() and equals()

Mock

thenReturn(T valueToBeReturned)

thenThrow(Throwable toBeThrown)

thenThrow(Class<? extends Throwable> toBeThrown)

then(Answer answer)

thenAnswer(Answer answer)

thenCallRealMethod()

doThrow(Throwable toBeThrown)

doThrow(Class<? extends Throwable> toBeThrown)

doAnswer(Answer answer)

doCallRealMethod()

doNothing()

doReturn(Object toBeReturned)

Argument Matching

any()

any(Class<T> clazz)

anyBoolean(), anyByte(), anyChar()

anyDouble(), anyFloat(), anyInt()

anyLong(), anyShort(), anyString()

anyCollection(), anyList(),anyMap()

anySet()

anyCollectionOf(Class<T> clazz), anyListOf(Class<T> clazz)

anyMapOf(Class<T> clazz),

anySetOf(Class<T> clazz)

anyVararg()

eq(T value)

isNull, isNull(Class<T> clazz)

isNotNull, isNotNull(Class<T> clazz)

isA(Class<T> clazz)

refEq(T value, String... excludeFields)

matches(String regex)

startsWith(string),

endsWith(string),

contains(string)

aryEq(PrimitiveType value[])

aryEq(T[] value)

cmpEq(Comparable<T> value)

gt(value), geq(value), lt(value),

leq(value)

argThat(org.hamcrest.Matcher<T>

matcher)

booleanThat(Matcher<Boolean> matcher),

byteThat(matcher),

charThat(matcher),

doubleThat(matcher),

floatThat(matcher),

intThat(matcher),

longThat(matcher),

shortThat(matcher)

and(first, second), or(first, second),

not(first)

Verify

times(int wantedNumberOfInvocations)

never()

atLeastOnce()

atLeast(int minNumberOfInvocations)

atMost(int maxNumberOfInvocations)

only()

timeout(int millis)

★initMocksについて

@BeforeMethod

public void setup() {

MockitoAnnotations.initMocks(this);

}

同様

@BeforeClass

public void setup() {

MockitoAnnotations.initMocks(this);

}

@AfterMethod

public void setup() {

Mockito.reset(mock1, mock2);

}

★@Mockのanswerオプション

RETURNS_SMART_NULLS

@Mock(answer=Answers.RETURNS_SMART_NULLS)

private Foo mock;

Stuff stuff = mock.getStuff();

stuff.doSomething();

RETURNS_DEEP_STUBS

@Mock(answer=Answers.RETURNS_DEEP_STUBS)

private Foo mock;

when(mock.getBar().getName()).thenReturn("deep");

assertEquals("deep", mock.getBar().getName());

CALLS_REAL_METHODS

@Mock(answer=Answers.CALLS_REAL_METHODS)

private Foo mock;

value = mock.getSomething();

when(mock.getSomething()).thenReturn(fakeValue);

value = mock.getSomething();

★InOrder用法

@Mock

private List singleMock;

singleMock.add("first");

singleMock.add("second");

InOrder inOrder = Mockito.inOrder(singleMock);

inOrder.verify(singleMock).add("first");

inOrder.verify(singleMock).add("second");

@Mock

private List firstMock;

@Mock

private List secondMock;

firstMock.add("first");

secondMock.add("second");

InOrder inOrder = Mockito.inOrder(firstMock, secondMock);

inOrder.verify(firstMock).add("first");

inOrder.verify(secondMock).add("second");

Mockito

https://code.google.com/p/mockito/

<dependency>

<groupId>org.mockito</groupId>

<artifactId>mockito-all</artifactId>

<version>1.9.5</version>

<scope>test</scope>

</dependency>

基本的な使い方

public class MyService {

public String method() {

return "xxx";

}

public String anotherMethod() {

return method() + "," + method();

}

}

import static org.mockito.Mockito.*;

public class MyServiceTest {

@Test

public void testMock() {

// クラスごと(対象クラスの実装に依存せず)

MyService myService = mock(MyService.class);

when(myService.method()).thenReturn("xxx");

String actual = myService.method();

assertEquals(actual, "xxx");

// 特定のメソッドのみ(対象クラスの実装に依存)

MyService myService = spy(new MyService());

when(myService.method()).thenReturn("xxx");

String actual = myService.anotherMethod();

assertEquals(actual, "yyy");

}

}

サンプル

public class SampleA {

private SampleB sampleB;

private SampleC sampleC;

public String createName() {

return sampleB.getName("xxx");

}

public String createPrice() {

int id = sampleC.getId();

int price = sampleC.getPrice(id);

return "id:" + id + " price:" + price;

}

}

import static org.mockito.Matchers.*;

import static org.mockito.Mockito.*;

import static org.hamcrest.Matchers.*;

public class SampleTest {

@Mock

private SampleB sampleBMock;

@Spy

private SampleC sampleCMock = new SampleC();

@InjectMocks //テスト対象

private SampleA sampleA = new SampleAImpl();

@InjectMocks //複数の@InjectMocksが使える;フィールドもMockできる

@Spy

private SampleD sampleD = new SampleDImpl();

※@InjectMocksと@Spyは一緒に使えるが、@InjectMocksと@Mockは×

@Before

public void setup() {

MockitoAnnotations.initMocks(this);

}

@Test

public void testVerifyMock() {

when(sampleBMock.get(0)).thenReturn("first");

when(sampleBMock.get(1)).thenThrow(new RuntimeException());

when(sampleBMock.getName(anyString())).thenReturn("mock");

//連続

when(sampleBMock.someMethod("xxx"))

.thenThrow(new RuntimeException())

.thenReturn("foo");

when(sampleBMock.someMethod("xxx"))

.thenReturn("one", "two", "three");

String ret = sampleA.createName();

assertEquals("mock", ret);

verify(sampleBMock, times(1)).getName("xxx");

verify(sampleBMock).getName("xxx"); // times(1)はデフォルト

verify(sampleBMock, never()).getName("never");

verify(sampleBMock, atLeastOnce()).add("three times");

verify(sampleBMock, atLeast(2)).add("five times");

verify(sampleBMock, atMost(5)).add("three times");

verify(sampleBMock).get(anyInt());

verify(sampleBMock).someMethod(

anyInt(), anyString(), eq("third argument"));

verify(sampleBMock, timeout(100)).someMethod();

verify(sampleBMock, timeout(100).times(2)).someMethod();

// voidメソッドのスタブ化

doThrow(new RuntimeException()).when(sampleBMock).clear();

doNothing()

.doThrow(new RuntimeException())

.when(sampleBMock).someVoidMethod();

//上書きの場合

when(sampleBMock.foo()).thenThrow(new RuntimeException());

// when(sampleBMock.foo()).thenReturn("bar"); //×

doReturn("bar").when(sampleBMock).foo();

}

@Test

public void testVerifySpy() {

doReturn(100).when(sampleCMock).getPrice(anyInt());

String ret = sampleA.createPrice();

assertEquals("id:2 price:100", ret);

when(sampleCMock.size()).thenReturn(100);

List list = new LinkedList();

List spy = spy(list);

//when(spy.get(0)).thenReturn("foo"); //例外

doReturn("foo").when(spy).get(0);

assertThat(list.get(0), is("foo"));

}

}

※後置記法と前置記法(NOTE:戻り値がvoidのメソッドは前置記法で)

when(mock.get(0)).thenReturn("xxx");

when(mock.get(1)).thenThrow(new IndexOutOfBoundsException());

doReturn("xxx").when(mock).get(0);

doThrow(new IndexOutOfBoundsException()).when(mock).get(1);

★ArgumentCaptor用法

基本

@Mock

private List mock;

@Captor

private ArgumentCaptor<MyClass> captor;

mock.add(new MyClass("foo"));

//verify(mock).add(new MyClass("foo"));

verify(mock).add(captor.capture());

MyClass argument = captor.getValue();

assertEquals("foo", argument.getName());

サンプル

public class PersonService{

@Inject

private PersonDao personDao;

public boolean update(Integer personId, String name){

Person person = personDao.findPerson(personId);

if(person != null){

Person updatedPerson =

new Person(person.getPersonID(), name);

personDao.update(updatedPerson);

return true;

}

return false;

}

}

import static org.junit.Assert.*;

import org.mockito.ArgumentCaptor;

import org.mockito.Mock;

import org.mockito.InjectMocks;

import org.mockito.MockitoAnnotations;

public class PersonServiceTest{

@Mock

private PersonDao personDAO;

@InjectMocks

private PersonService personService;

@Captor

private ArgumentCaptor<Person> personCaptor;

@Before

public void setUp() throws Exception{

MockitoAnnotations.initMocks(this);

}

@Test

public void shouldUpdatePersonName(){

Person person = new Person(101, "xxx");

when(personDAO.findPerson(101)).thenReturn(person);

boolean updated = personService.update(101, "yyy");

assertTrue(updated);

verify(personDAO).findPerson(101);

verify(personDAO).update(personCaptor.capture());

Person updatedPerson = personCaptor.getValue();

assertEquals("yyy", updatedPerson.getPersonName());

verifyNoMoreInteractions(personDAO);

}

@Test

public void shouldNotUpdateIfPersonNotFound(){

when(personDAO.findPerson(101)).thenReturn(null);

boolean updated = personService.update(101, "yyy");

assertFalse(updated);

verify(personDAO).findPerson(101);

verifyZeroInteractions(personDAO);

verifyNoMoreInteractions(personDAO);

}

}

★Answer用法

サンプル1

@Spy

private Logger logger;

final StringBuilder sb = new StringBuilder();

doAnswer(new Answer<Void>() {

@Override

public Void answer(InvocationOnMock invocation) throws Throwable {

sb.append(invocation.getArguments()[0]);

invocation.callRealMethod();

return null;

}

}).when(logger).info(anyString());

assertThat(sb.toString(), is("xxx"));

サンプル2

@Mock

private Sample mock;

doAnswer(new Answer<Object>() {

@Override

public Object answer(InvocationOnMock invocation) throws Throwable {

theString = (String) invocation.getArguments()[0];

return null;

}

}).when(mock).setString(anyString());

when(mock.getString()).thenAnswer(new Answer<String>() {

@Override

public String answer(InvocationOnMock invocation) throws Throwable {

return theString;

}

});

mock.setString("foo");

assertEquals("foo", mock.getString());

サンプル3

@Inject

private PersonJobDao personJobDao;

public PersonJob createPersonJob(Person person, Job job) {

...

PersonJob personJob = new PersonJob(person, job);

return personJobDao.create(personJob);

}

@Mock

private PersonJobDao personJobDao;

@Test

public void shouldXxx() {

when(personJobDao.create(any(PersonJob.class)))

.thenAnswer(new Answer<PersonJob>() {

public PersonJob answer(InvocationOnMock invocation)

throws Throwable {

return (PersonJob) invocation.getArguments()[0];

}

});

Person person = new Person();

Job job = new Job();

PersonJob personJob = personJobManager.createPersonJob(person, job);

assertThat(personJob.getPerson(), is(person));

assertThat(personJob.getJob(), is(job));

}