public interface PrintTest {
void print();
}
public class PrintTestgDummy implements PrintTest {
@Override
public void print() {
// 아무런 동작을 하지 않는다.
}
}
실제 객체는 printTest 인터페이스의 구현체를 필요하지만, 특정 테스트에서는 해당 구현체의 동작이 필요하지 않을 수 있다.
이처럼 동작하지 않아도 테스트에 영향을 미치지 않는 객체를 Dummy 객체라고 한다.
@Entity
public class User {
@Id
private Long id;
private String name;
protected User() {}
public User(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return this.id;
}
public String getName() {
return this.name;
}
}
public interface UserRepository {
void save(User user);
User findById(long id);
}
public class FakeUserRepository implements UserRepository {
private Collection<User> users = new ArrayList<>();
@Override
public void save(User user) {
if (findById(user.getId()) == null) {
user.add(user);
}
}
@Override
public User findById(long id) {
for (User user : users) {
if (user.getId() == id) {
return user;
}
}
return null;
}
}
테스트해야 하는 객체가 데이터베이스와 연관되어 있다면, 이를 가짜 데이터베이스 역할을 하는 FakeUserRepository를 만들어 테스트 객체에 주입한다.
이를 통해, 테스트 객체는 데이터베이스에 의존하지 않으면서도 동일하게 동작을 하는 Fake 객체를 만들 수 있다.
public interface UserRepository {
void save(User user);
User findById(long id);
}
public class StubUserRepository implements UserRepository {
// ...
@Override
public User findById(long id) {
return new User(id, "Test User");
}
}
StubUserRepository 의 findById() 메서드를 호출 할 경우, 언제나 동일한 id 값에 Test User라는 이를을 가진 인스턴스를 반환 받게 된다.
이처럼 테스트를 위해 의도한 결과만 반환되도록 하기 위한 객체가 stub 이다.
public class MailingSpyService implements MailingService {
private int sendMailCount = 0; // spy 추가
private Collection<Mail> mails = new ArrayList<>();
@Override
public void sendMail(Mail mail) {
sendMailCount++; // spy 추가
mails.add(mail);
}
// spy 추가
public long getSendMailCount() {
return sendMailCount;
}
}
MailingSpyService는 sendMail을 호출 횟수를 물어볼때, getSendMailCount 메서드를 통해 sendMailCount 를 확인 할 수 있다.
이처럼 자기 자신이 호출된 상황을 확인할 수 있는 객체가 Spy이다.
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
@Mock
private UserRepository userRepository;
@Test
void test() {
when(userRepository.findById(anyLong())).thenReturn(new User(1, "Test User"));
User actual = userService.findById(1);
assertThat(actual.getId()).isEqualTo(1);
assertThat(actual.getName()).isEqualTo("Test User");
}
}
userService 인터페이스 구현체가 findById() 메서드를 호출하였을 때 어떤 결과를 반환할지 결정할 수 있다.
Java에서는 보통 외부와의 해당 기술들을 사용하기 위해 JUnit과 Mockito 프레임워크를 함께 사용한다.
@Mock: Mock 객체를 만들어 반환해주는 어노테이션
@Spy: Stub하지 않은 메소드들은 원본 메소드 그대로 사용하는 어노테이션
@InjectMocks: @Mock 또는 @Spy로 생성된 가짜 객체를 자동으로 주입시켜주는 어노테이션
public class MembmerService{
private MemberDao dao;
public Member createMember(Member member){
if(dao.getMemberCount(member) > 0){
...
throw new Exception();
}
...
}
}
public class MemberDao{
public int getMemberCount(Membmer member){
}
}
// JUnit과 함께 사용하기 위해 테스트 클래스에 아래 annotation 추가
@ExtendWith(MockitoExtension.class)
class MemberTest {
@Mock
MemberDao memberDao;
@InjectMocks
MemberService memberService;
@Test
public void test(){
when(memberDao.getMemberCount()).thenReturn(0); //mock에 대한 기대 명세
Member member = new Member("corn", 25);
assertThat(memberService.createMember(member), is(member));
}
...
}
Reference