サーチ…
シンプルモック
Mockitoは、(最終的ではない)クラスとインターフェースのモックを作成するための、すべてのサイズのワンサイズのメッシュを提供します。
Dependency mock = Mockito.mock(Dependency.class);
これは、 Dependency
がインタフェースかクラスかにかかわらず、 Dependency
模擬インスタンスを作成します。
その後、Mockito.when(x).thenReturn(y)表記法を使用して、そのモックへのメソッド呼び出しをスタブすることができます。
Mockito.when(mock.possiblyBuggyMethod()).thenReturn("someString");
したがって、 Dependency.possiblyBuggyMethod()
呼び出しは単に"someString"
返します。
タイプセーフではないため、ほとんどのユースケースでは推奨されない別の表記法があります。
Mockito.doReturn("someString").when(mock).possiblyBuggyMethod()
デフォルトのモック
シンプルモックはすべての呼び出しにnull(またはプリミティブのデフォルト)を返しますが、その動作を変更することは可能です。
Dependency mock = Mockito.mock(Dependency.class, new Answer() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
return "someString";
}
});
またはラムダを使用して:
Dependency mock = Mockito.mock(Dependency.class, (Answer) invocationOnMock -> "someString");
この例では、すべての呼び出しに対して「someString」が返されますが、answer-methodで任意のロジックを定義することは可能です。
アノテーションを使用してクラスを非難する
テスト中のクラス:
public class GreetingsService { // class to be tested in isolation
private UserService userService;
public GreetingsService(UserService userService) {
this.userService = userService;
}
public String getGreetings(int userId, LocalTime time) { // the method under test
StringBuilder greetings = new StringBuilder();
String timeOfDay = getTimeOfDay(time.getHour());
greetings.append("Good ").append(timeOfDay).append(", ");
greetings.append(userService.getFirstName(userId)) // this call will be mocked
.append(" ")
.append(userService.getLastName(userId)) // this call will be mocked
.append("!");
return greetings.toString();
}
private String getTimeOfDay(int hour) { // private method doesn't need to be unit tested
if (hour >= 0 && hour < 12)
return "Morning";
else if (hour >= 12 && hour < 16)
return "Afternoon";
else if (hour >= 16 && hour < 21)
return "Evening";
else if (hour >= 21 && hour < 24)
return "Night";
else
return null;
}
}
このインタフェースの動作は嘲笑されます:
public interface UserService {
String getFirstName(int userId);
String getLastName(int userId);
}
UserService
実際の実装を想定しUserService
。
public class UserServiceImpl implements UserService {
@Override
public String getFirstName(int userId) {
String firstName = "";
// some logic to get user's first name goes here
// this could be anything like a call to another service,
// a database query, or a web service call
return firstName;
}
@Override
public String getLastName(int userId) {
String lastName = "";
// some logic to get user's last name goes here
// this could be anything like a call to another service,
// a database query, or a web service call
return lastName;
}
}
MockitoのJunitテスト:
public class GreetingsServiceTest {
@Mock
private UserServiceImpl userService; // this class will be mocked
@InjectMocks
private GreetingsService greetingsService = new GreetingsService(userService);
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testGetGreetings_morning() throws Exception {
// specify mocked behavior
when(userService.getFirstName(99)).thenReturn("John");
when(userService.getLastName(99)).thenReturn("Doe");
// invoke method under test
String greetings = greetingsService.getGreetings(99, LocalTime.of(0, 45));
Assert.assertEquals("Failed to get greetings!", "Good Morning, John Doe!", greetings);
}
@Test
public void testGetGreetings_afternoon() throws Exception {
// specify mocked behavior
when(userService.getFirstName(11)).thenReturn("Jane");
when(userService.getLastName(11)).thenReturn("Doe");
// invoke method under test
String greetings = greetingsService.getGreetings(11, LocalTime.of(13, 15));
Assert.assertEquals("Failed to get greetings!", "Good Afternoon, Jane Doe!", greetings);
}
}
部分的な嘲笑のための「スパイ」
@Spyアノテーション (またはメソッド )を使用して、オブジェクトを部分的にモックすることができます。これは、クラスの動作を部分的にモックする場合に便利です。たとえば、2つの異なるサービスを使用するクラスがあり、そのうちの1つだけを疑似し、他のサービスの実際の実装を使用するとします。
補足説明:哲学的には、実際のクラスを統合し、テスト対象のクラスを完全に分離してテストしないので、これを真の意味で「純粋な単体テスト」とは考えません。それにもかかわらず、これは現実世界で実際に有用であり、実際のDAOを使用できるようにメモリデータベースの実装を使用してデータベースをモックするときによく使用します。
Class under test:
public class GreetingsService { // class to be tested in isolation
private UserService userService;
private AppService appService;
public GreetingsService(UserService userService, AppService appService) {
this.userService = userService;
this.appService = appService;
}
public String getGreetings(int userId, LocalTime time) { // the method under test
StringBuilder greetings = new StringBuilder();
String timeOfDay = getTimeOfDay(time.getHour());
greetings.append("Good ").append(timeOfDay).append(", ");
greetings.append(userService.getFirstName(userId)) // this call will be mocked
.append(" ")
.append(userService.getLastName(userId)) // this call will be mocked
.append("!");
greetings.append(" Welcome to ")
.append(appService.getAppName()) // actual method call will be made
.append(".");
return greetings.toString();
}
private String getTimeOfDay(int hour) { // private method doesn't need to be unit tested
if (hour >= 0 && hour < 12)
return "Morning";
else if (hour >= 12 && hour < 16)
return "Afternoon";
else if (hour >= 16 && hour < 21)
return "Evening";
else if (hour >= 21 && hour < 24)
return "Night";
else
return null;
}
}
このインタフェースの動作は嘲笑されます:
public interface UserService {
String getFirstName(int userId);
String getLastName(int userId);
}
UserService
実際の実装を想定しUserService
。
public class UserServiceImpl implements UserService {
@Override
public String getFirstName(int userId) {
String firstName = "";
// some logic to get user's first name
// this could be anything like a call to another service,
// a database query, or a web service call
return firstName;
}
@Override
public String getLastName(int userId) {
String lastName = "";
// some logic to get user's last name
// this could be anything like a call to another service,
// a database query, or a web service call
return lastName;
}
}
このインタフェースの動作は嘲笑されません:
public interface AppService {
String getAppName();
}
AppService
実際の実装をAppService
ます:
public class AppServiceImpl implements AppService {
@Override
public String getAppName() {
// assume you are reading this from properties file
String appName = "The Amazing Application";
return appName;
}
}
MockitoのJunitテスト:
public class GreetingsServiceTest {
@Mock
private UserServiceImpl userService; // this class will be mocked
@Spy
private AppServiceImpl appService; // this class WON'T be mocked
@InjectMocks
private GreetingsService greetingsService = new GreetingsService(userService, appService);
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testGetGreetings_morning() throws Exception {
// specify mocked behavior
when(userService.getFirstName(99)).thenReturn("John");
when(userService.getLastName(99)).thenReturn("Doe");
// invoke method under test
String greetings = greetingsService.getGreetings(99, LocalTime.of(0, 45));
Assert.assertEquals("Failed to get greetings!", "Good Morning, John Doe! Welcome to The Amazing Application.", greetings);
}
@Test
public void testGetGreetings_afternoon() throws Exception {
// specify mocked behavior
when(userService.getFirstName(11)).thenReturn("Jane");
when(userService.getLastName(11)).thenReturn("Doe");
// invoke method under test
String greetings = greetingsService.getGreetings(11, LocalTime.of(13, 15));
Assert.assertEquals("Failed to get greetings!", "Good Afternoon, Jane Doe! Welcome to The Amazing Application.", greetings);
}
}
モックされたオブジェクトにプライベートフィールドを設定する
テスト中のクラスでは、コンストラクタを経由してもアクセスできないプライベートフィールドがあるかもしれません。このような場合は、リフレクションを使用してそのようなプロパティを設定できます。これは、このようなJUnitテストのスニペットです。
@InjectMocks
private GreetingsService greetingsService = new GreetingsService(); // mocking this class
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
String someName = "Some Name";
ReflectionTestUtils.setField(greetingsService, // inject into this object
"name", // assign to this field
someName); // object to be injected
}
ここではSptringのReflectionTestUtils.setField(Object targetObject, String name, Object value)
メソッドを使用して簡素化していますが、これを行うには古いJava Reflectionを使用することができます。