수색…
단순 모의
Mockito는 (최종적이지 않은) 클래스와 인터페이스의 모의 객체를 생성 할 수있는 단일 크기의 메소드를 제공합니다.
Dependency mock = Mockito.mock(Dependency.class);
Dependency
이 인터페이스인지 클래스인지에 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";
}
});
또는 lambdas 사용 :
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
의 실제 구현을 가정합니다.
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 주석 (또는 메서드 )은 객체를 부분적으로 모의하는 데 사용할 수 있습니다. 이것은 클래스의 동작을 부분적으로 모의 할 때 유용합니다. 예를 들어 두 개의 다른 서비스를 사용하는 클래스가 있고 그 중 하나만 조롱하고 다른 서비스의 실제 구현을 사용한다고 가정합니다.
참고 사항 : 철학적으로는 실제 클래스를 통합하고 테스트중인 클래스를 완전하게 격리하지 않으므로이 클래스를 "순수한 단위 테스트"로 간주하지는 않습니다. 그럼에도 불구하고 이것은 실제 세계에서 실제로 유용 할 수 있으며 실제 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);
}
}
조롱 된 객체에 비공개 필드 설정
테스트중인 클래스에서 생성자를 통해 액세스 할 수없는 일부 비공개 필드가있을 수 있습니다. 그런 경우 리플렉션을 사용하여 이러한 속성을 설정할 수 있습니다. 이것은 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을 사용하여 동일한 작업을 수행 할 수 있습니다.