elevne's Study Note
토비의 Spring (8: 서비스 추상화(2)) 본문
기존에 작성한 UserService 에 새로운 기능을 추가한다. 고객의 레벨이 업그레이드 될 때 해당 사용자에게 업그레이드 안내 메일을 보내는 것이다. 유저의 레벨을 업그레이드하는 upgradeLevel() 메서드 끝에 새로 만드는 메일을 보내는 메서드를 추가해주면 될 것이다.
private MailSender mailSender;
public void sendUpgradeEmail(User user) {
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setTo(user.getEmail());
mailMessage.setFrom("useradmin@ksug.org");
mailMessage.setSubject("UPGRADE 안내");
mailMessage.setText("사용자의 등급이 "+user.getLevel().name());
this.mailSender.send(mailMessage);
}
@Bean
public MailSender mailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("mail.server.com");
return mailSender;
}
위 메서드를 테스트하면 예외가 발생하는 것을 확인할 수 있다. 메일 서버가 준비되어 있지 않기 때문에 당연한 결과이다. 테스트를 수행할 때 메일이 전송될 수 있게끔 실제로 메일 서버를 준비하여 테스트를 진행해볼 수도 있다.
하지만 이러한 방식은 대개 바람직하지 못하다고 한다. 메일 발송은 서버에 큰 부하를 주는 작업이고, 실제 운영 중인 메일 서버를 통해 테스트를 실행할 때마다 메일을 보내게 되면 서버에 상당한 부담이 될 수 있다. 게다가 메일이 실제로 발송되어 버린다는 문제도 생긴다. (물론 테스트용으로 준비한 특별 메일 주소로만 보낼 수 있긴 하다) 대신 다른 테스트용 메일 서버를 이용하는 방법도 있긴 하다. 하지만 메일 발송 기능은 어디까지나 업그레이드 작업의 보조적인 기능이다. (게다가 메일이 정말 잘 도착했는지 확인하는 것은 어렵다. 메일 발송용 서버에 문제 없이 전달되었는지 확인하는 것이 한계)
메일 서버는 충분히 테스트된 시스템이기에 SMTP 로 메일 전송 요청을 받으면 별문제 없이 메일이 잘 전송됐다고 믿어도 충분하다고 한다. 고로 테스트 가능한 메일 서버까지만 잘 전송되는지 확인하면 된다. (그리고 테스트용 메일 서버는 메일 전송 요청은 받지만 실제로 메일을 발송하지는 않게끔 하는 것)
여기서, SMTP 라는 표준 메일 발송 프로토콜로 메일 서버에 요청이 전달되기만 하면 메일이 발송될 것이라 믿고, 준비한 메일 서버를 사용해 테스트해도 좋다면, 그 똑같은 원리를 UserService 와 JavaMail 사이에도 적용할 수 있을 것이다. JavaMail 을 직접 구동시키는 것이 아니라 JavaMail 을 대신할 수 있는, JavaMail 과 동일한 인터페이스를 갖는 코드가 동작하도록 만드는 것이다. MailSender 인터페이스를 사용하면 되는 것이다.
public class DummyMailSender implements MailSender {
public void send(SimpleMailMessage mailMessage) throws MailException {}
public void send(SimpleMailMessage[] mailMessages) throws MailException {}
}
위 DummyMailSender 은 MailSender 인터페이스를 구현했을 뿐, 하는 작업이 전혀 없다. Bean 에서도 JavaMailSenderImpl 대신 DummyMailSender 을 사용하도록 변경해준다. 그 다음 테스트 코드에서 이를 사용해볼 수 있다.
@Test
public void 메일테스트() {
userService.sendUpgradeEmail(user1);
}
성공적으로 테스트가 완료되는 것을 확인할 수 있을 것이다. DummyMailSender 을 사용하면 메일이 메일 서버로 발송되지 않는다. 메일 발송 기능 자체는 MailSender 에 대한 별도의 테스트 또는 메일 서버 점검용 테스트를 만들어 확인해볼 수 있다. DummyMailSender 은 하는 일이 없지만, 그 가치는 매우 크다고 한다. 이 클래스를 이용해 JavaMail 로 메일을 직접 발송하는 클래스를 대치하지 않는다면 테스트는 매우 불편해진다. 이렇게 테스트 대상이 되는 오브젝트의 기능에만 충실하게 수행하면서 빠르게 자주 테스트를 실행할 수 있도록 사용하는 오브젝트를 통틀어 테스트 대역이라고 한다.
대표적인 테스트 대역으로 테스트 스텁이 있다. 테스트 스텁은 테스트 대상 오브젝트의 의존객체로서 존재하면서 테스트 동안에 코드가 정상적으로 수행할 수 있도록 돕는 것을 말한다. 일반적으로 이러한 테스트 스텁은 DI 등을 통해 미리 의존 오브젝트로 받아 테스트 코드 내부에서 간접적으로 사용된다.
테스트 대상 오브젝트 메서드가 돌려주는 결과에 더해 테스트 오브젝트가 간접적으로 의존 오브젝트에 넘기는 값과 그 행위 자체에 대해서도 검증하고자 한다면 Mock Object 를 사용하면 된다. 이는 스텁처럼 테스트 오브젝트가 정상적으로 실행되도록 도와주며 테스트 오브젝트와 자신의 사이에서 일어나는 커뮤니케이션 내용을 저장해뒀다가 테스트 결과를 검증하는데 사용할 수 있게끔 해준다.
public class MockMailSender implements MailSender {
private List<String> requests = new ArrayList<>();
public List<String> getRequests() {
return this.requests;
}
public void send(SimpleMailMessage message) throws MailException {
requests.add(message.getTo()[0]);
}
public void send(SimpleMailMessage[] messages) throws MailException {}
}
위와 같이 수신자 메일 주소를 미리 준비해둔 리스트에 저장하는 기능이 추가된 것이다. 그리고 해당 값들은 getRequests() 메서드를 통해 확인해볼 수 있다. 아래와 같이 작성하여 테스트를 진행해본다.
@Test
public void 목객체테스트() {
for (User user : this.users) userDao.add(user);
userService.upgradeLevels();
MockMailSender mailSender = (MockMailSender) userService.getMailSender();
List<String> requests = mailSender.getRequests();
System.out.println(requests);
Assert.assertThat(requests.size(), Is.is(2));
Assert.assertThat(requests.get(0), Is.is(users.get(1).getEmail()));
Assert.assertThat(requests.get(1), Is.is(users.get(3).getEmail()));
}
MockMailSender 또한 테스트에 앞서 스프링 설정을 통해 UserService 에 DI 해주었다. 위와 같이 목 오브젝트는 작성하기는 간단하면서 기능은 막강하다. 보통의 테스트 방법으로는 검증하기가 까다로운 테스트 대상 오브젝트의 내부에서 일어나는 일이나 다른 오브젝트 사이에서 주고받는 정보까지 검증할 수 있는 것이다.
Reference:
토비의 스프링
'Backend > Toby Spring' 카테고리의 다른 글
토비의 Spring (9: AOP(2)) (0) | 2023.06.07 |
---|---|
토비의 Spring (9: AOP(1)) (0) | 2023.05.22 |
토비의 Spring (8: 서비스 추상화(1)) (0) | 2023.05.16 |
토비의 Spring (7: Exceptions (2)) (0) | 2023.05.15 |
토비의 Spring (7: Exceptions (1)) (0) | 2023.05.11 |