間諜部分嘲笑
@Spy 註釋 (或方法 )可用於部分模擬物件。當你想要部分模擬類的行為時,這非常有用。例如,假設你有一個使用兩個不同服務的類,並且你只想模擬其中一個並使用其他服務的實際實現。
旁註:儘管哲學上我不認為這是真正意義上的純單元測試,因為你正在整合一個真正的類而不是在完全隔離的情況下測試你的測試類。然而,這在現實世界中實際上是有用的,當我使用記憶體資料庫實現中的一些模擬資料庫時,我經常使用它,以便我可以使用真正的 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
的實際實現:
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
的實際實現:
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);
}
}