Giới thiệu
Khi viết Unit Test, chúng ta cần thực hiện một số Assert để xác nhận expected result và actual result là như nhau. Đối với các mock object, chúng ta cũng cần verify một vài behavior đã được gọi hay chưa.
Trong Mockito, chúng ta có thể thực hiện verify các mock object thông qua phương thức Mockito.verfify():
verify(mockedObject, times(2)).someMethodOfMockObject(argurments);
Một số ví dụ Verifying Behavior
Thông thường, viết Unit Test sử dụng Mockito bao gồm các bước cơ bản sau:
- Tạo mock object.
- Điều khiển một mock object và xác định phải làm gì khi các phương thức cụ thể của mock object được gọi.
- Gọi phương thức.
- Verify phương thức đã được gọi hay chưa.
Ví dụ:
// 1: Create mock object
List mock = mock(List.class);
// 2: Stub - Control mock’s behavior
when(mock.add("gpcoder")).thenReturn(true);
// 3: Call behavior
mock.add("gpcoder");
// 4: Verify behavior
verify(mock).add("gpcoder");
Verify số lần phương thức được gọi
Một số phương thức được sử dụng để verify số lần gọi:
- times(number) : verify chính xác số lần phương thức được gọi.
- never() : verify phương thức không bao giờ được gọi.
- atLeastOnce() : verify phương thức được gọi ít nhất 1 lần.
- atLeast(number) : verify phương thức được gọi ít nhất number lần.
- atMost(number) : verify phương thức được gọi nhiều nhất number lần.
- verifyNoMoreInteractions(mockedObjects) : xác nhận rằng không có bất kỳ một phương thức nào của mockedObject được gọi mà không được gọi verfify() để kiểm tra.
- verifyZeroInteractions() :tương tự như verifyNoMoreInteractions().
package com.gpcoder.mockito.verify;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class VerifyTest {
private List<String> mockedList;
@Before
public void prepareForTest() {
// Mock creation
mockedList = Mockito.mock(List.class);
}
@Test
public void zeroTimeTest() {
// Verifies certain behavior never happened
Mockito.verify(mockedList, Mockito.never()).add("gpcoder.com");
Mockito.verify(mockedList, Mockito.times(0)).add("gpcoder.com");
}
@Test
public void oneTimeTest() {
// Using mock object
mockedList.add("gpcoder.com");
// Verifies certain behavior happened once
Mockito.verify(mockedList).add("gpcoder.com"); // Default one time
Mockito.verify(mockedList, Mockito.times(1)).add("gpcoder.com");
}
@Test
public void atLeastTest() {
// Using mock object
mockedList.add("one");
mockedList.add("two");
mockedList.add("three");
// Verifies certain behavior happened at least once
Mockito.verify(mockedList, Mockito.atLeastOnce()).add(Mockito.anyString());
// Verifies certain behavior happened at least twice
Mockito.verify(mockedList, Mockito.atLeast(2)).add(Mockito.anyString());
}
@Test
public void atMostTest() {
// Using mock object
mockedList.add("one");
mockedList.add("two");
// Verifies certain behavior happened at most twice
Mockito.verify(mockedList, Mockito.atMost(2)).add(Mockito.anyString());
}
@Test
public void verifyNoMoreInteractionsTest() {
// Create mock object
List<String> mockOne = Mockito.mock(List.class);
// Using mocks - only mockOne is interacted
mockOne.add("one");
// Uncomment this code will make the test case failed because it is unverified interaction
// mockOne.get(0);
// Ordinary verification
Mockito.verify(mockOne).add("one");
// Checks if any of given mocks has any unverified interaction. You can use this
// method after you verified your mocks - to make sure that nothingelse was
// invoked on your mocks.
Mockito.verifyNoMoreInteractions(mockOne);
}
@Test
public void verifyZeroInteractionsTest() {
// Create mock object
List<String> mockOne = Mockito.mock(List.class);
List<String> mockTwo = Mockito.mock(List.class);
List<String> mockThree = Mockito.mock(List.class);
// Using mocks - only mockOne is interacted
mockOne.add("one");
// Ordinary verification
Mockito.verify(mockOne).add("one");
// Verify that method was never called on a mock
Mockito.verify(mockOne, Mockito.never()).add("two");
// Verifies that no interactions happened on given mocks beyond the previously
// verified interactions
Mockito.verifyZeroInteractions(mockTwo, mockThree);
}
@Test
public void clearInvocationsTest() {
// Create mock object
List<String> mockOne = Mockito.mock(List.class);
List<String> mockTwo = Mockito.mock(List.class);
// Using mocks - only mockOne is interacted
mockOne.add("one");
mockTwo.add("one");
// Use this method in order to only clear invocations, when stubbing is non-trivial.
Mockito.clearInvocations(mockOne, mockTwo);
// Another way: reset() a mock so that it can be reused later
// Mockito.reset(mockOne, mockTwo);
// Verifies that no interactions happened on given mocks beyond the previously
// verified interactions
Mockito.verifyZeroInteractions(mockOne, mockTwo);
}
}
Verify các tham số (argument) của phương thức
Đối với các void method, thông thường chúng ta có thể verify các thông tin:
- Số lần phương thức được gọi.
- Giá trị các tham số.
Để verify các tham số (argument) của các phương thức, chúng ta có thể sử dụng Argument Matcher hoặc Argument Captor.
package com.gpcoder.mockito.verify;
import static org.hamcrest.Matchers.hasItem;
import java.util.Arrays;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
public class ArgumentMatcherTest {
@Captor
private ArgumentCaptor<List<String>> captor;
@Mock
private List<String> mockedList;
@Before
public void prepareForTest() {
MockitoAnnotations.initMocks(this);
}
@Test
public final void argumentCaptorTest() {
List<String> asList = Arrays.asList("gpcoder.com", "mockito", "junit");
mockedList.addAll(asList);
// Verify value on arguments
Mockito.verify(mockedList).addAll(captor.capture());
final List<String> capturedArgument = captor.getValue();
Assert.assertEquals(3, capturedArgument.size());
Assert.assertThat(capturedArgument, hasItem("gpcoder.com"));
}
@Test
public void argumentMatcherTest() {
List<String> asList = Arrays.asList("gpcoder.com", "mockito", "junit");
mockedList.addAll(asList);
// Verifies certain behavior happened at at least once with the given value is
// "gpcoder.com"
Mockito.verify(mockedList).addAll(Mockito.anyCollection());
Mockito.verify(mockedList, Mockito.atLeast(1))
.addAll(Mockito.argThat(collection -> collection.contains("gpcoder.com")));
}
}
Verify thứ tự phương thức được gọi
Trong một số trường hợp, chúng ta cần verify thứ tự thực thi các phương thức. Chẳng hạn, chức năng tạo user bao gồm các bước sau:
- Tạo mã user.
- Lưu thông tin user vào cơ sở dữ liệu.
- Gửi mail xác nhận.
- Hiển thị thông báo kết quả thêm thành công/ thất bại.
Trong Mockito, để verify thứ tự các phương thức được gọi chúng ta có thể sử dụng các phương thức hỗ trợ của class InOrder. Để tạo InOrder, chúng ta sử dụng phương thức Mockito.inOrder(mockedObjects).
UserCreationTemplate.java
package com.gpcoder.mockito.verify;
public abstract class UserCreationTemplate {
public void createUser() {
createUserCode();
saveUser();
sendEmail();
showResult();
}
protected abstract String createUserCode();
protected abstract void saveUser();
protected abstract void sendEmail();
protected abstract void showResult();
}
InOrderTest.java
package com.gpcoder.mockito.verify;
import java.util.List;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
public class InOrderTest {
@Test
public void singleMockTest() {
// Create mock object
List<String> mockedList = Mockito.mock(List.class);
// Using mock object
mockedList.add("was added first");
mockedList.add("was added second");
// Create an inOrder verifier for a single mock
InOrder inOrder = Mockito.inOrder(mockedList);
// Following will make sure that add is first called with "was added first",
// then with "was added second"
inOrder.verify(mockedList).add("was added first");
inOrder.verify(mockedList).add("was added second");
}
@Test
public void multipleMocksTest() {
// Create mock object
List<String> mockedList1 = Mockito.mock(List.class);
List<String> mockedList2 = Mockito.mock(List.class);
// Using mock object
mockedList1.add("was added first for 1st list");
mockedList2.add("was added first for 2nd list");
mockedList1.add("was added second for 1st list");
// Create inOrder object passing any mocks that need to be verified in order
InOrder inOrder = Mockito.inOrder(mockedList1, mockedList2);
// Following will make sure that firstMock was called before secondMock
inOrder.verify(mockedList1).add(Mockito.anyString());
inOrder.verify(mockedList2).add(Mockito.anyString());
inOrder.verify(mockedList1).add(Mockito.anyString());
}
@Test
public void verifyOrderingExecutionTest() {
// Create mock object
UserCreationTemplate mockedObject = Mockito.spy(UserCreationTemplate.class);
// Using mock object
mockedObject.createUser();
// Create inOrder object passing any mocks that need to be verified in order
InOrder inOrder = Mockito.inOrder(mockedObject);
// Verify ordering execution
inOrder.verify(mockedObject).createUserCode();
inOrder.verify(mockedObject).saveUser();
inOrder.verify(mockedObject).sendEmail();
inOrder.verify(mockedObject).showResult();
}
}
Verify thời gian thực thi (timeout)
Trong mockito, chúng ta cũng có thể verify thời gian thực thi của các phươn thức chạy async thông qua 2 phương thức:
- timeout(miliseconds) : verify phương thức được được gọi trong một khoảng thời gian miliseconds được chỉ định.
- after(miliseconds) : tương tự như timeout. Tuy nhiên, timeout() sẽ thoát ngay lập tức khi một phương thức đã được verify, còn after() sẽ chờ cho đến khi hết thời gian được chỉ định mới thực hiện verify.
CustomList.java
package com.gpcoder.mockito.verify;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class CustomList {
public static final int TIME_TO_EXECUTE_IN_MILISECONDS = 50;
private List<String> list;
public List<String> getList() {
return list;
}
public boolean add(String item) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(TIME_TO_EXECUTE_IN_MILISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
getList().add(item);
}
});
t.start();
return true;
}
}
TimeoutTest.java
package com.gpcoder.mockito.verify;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
public class TimeoutTest {
@InjectMocks
private CustomList mockedList;
@Mock
private List<String> list;
@Before
public void prepareForTest() {
// Mock creation
MockitoAnnotations.initMocks(this);
}
/**
* Verification will be triggered after given amount of millis, allowing testing
* of async code.
*
* timeout() exits immediately with success when verification passes
*/
@Test
public void timeoutTest() {
// Using mock object
mockedList.add("gpcoder.com");
// Verify call to add method to be called once times within 100 ms
Mockito.verify(list, Mockito.timeout(100)).add("gpcoder.com"); // Default once times
Mockito.verify(list, Mockito.timeout(100).times(1)).add("gpcoder.com");
}
/**
* Verification will be triggered after given amount of millis, allowing testing
* of async code.
*
* after() awaits full duration to check if verification passes
*/
@Test
public void afterTest() {
// Using mock object
mockedList.add("gpcoder.com");
// Verify call to add method to be called once times within 100 ms
Mockito.verify(list, Mockito.after(100)).add("gpcoder.com"); // Default once times
Mockito.verify(list, Mockito.after(100).times(1)).add("gpcoder.com");
}
}
Thử thay đổi giá trị TIME_TO_EXECUTE_IN_MILISECONDS là 50, 100, 150 và chạy thử để xem các kết quả khác nhau.
Tài liệu tham khảo: