이 시리즈는 TDD를 숙달하기 전에 TEST 자체에 대한 이해를 높이기 위한 학습 시리즈입니다
@Transactional
public UserEntity create(UserCreate userCreate) {
UserEntity userEntity = new UserEntity();
userEntity.setEmail(userCreate.getEmail());
userEntity.setNickname(userCreate.getNickname());
userEntity.setAddress(userCreate.getAddress());
userEntity.setStatus(UserStatus.PENDING);
userEntity.setCertificationCode(UUID.randomUUID().toString());
userEntity = userRepository.save(userEntity);
String certificationUrl = generateCertificationUrl(userEntity);
sendCertificationEmail(userCreate.getEmail(), certificationUrl);
return userEntity;
}
private void sendCertificationEmail(String email, String certificationUrl) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email);
message.setSubject("Please certify your email address");
message.setText("Please click the following link to certify your email address: " + certificationUrl);
mailSender.send(message);
}
private String generateCertificationUrl(UserEntity userEntity) {
return "http://localhost:8080/api/users/" + userEntity.getId() + "/verify?certificationCode=" + userEntity.getCertificationCode();
}
UserService
에서 개선할 부분만 잘라낸 코드 부분입니다Service
레이어에 이메일에 코드를 전송하고, 인증 URL까지 생성하는 기능이 구현되어 있습니다JavaMailSender
를 의존하고 있는 형태입니다Service
로 분리해 보겠습니다Service
패키지 아래에 CertificationService
를 만들고 로직을 옮겨줍니다UserService
는 아래 모습으로 변경되었습니다 @Transactional
public UserEntity create(UserCreate userCreate) {
UserEntity userEntity = new UserEntity();
userEntity.setEmail(userCreate.getEmail());
userEntity.setNickname(userCreate.getNickname());
userEntity.setAddress(userCreate.getAddress());
userEntity.setStatus(UserStatus.PENDING);
userEntity.setCertificationCode(UUID.randomUUID().toString());
userEntity = userRepository.save(userEntity);
certificationService.send(userCreate.getEmail(), userEntity.getId(), userEntity.getCertificationCode());
return userEntity;
}
CertificationService
로 복잡한 로직을 분리해오긴 했지만, 그대로 코드를 옮긴다면 해당 서비스 레이어가 JavaMailSender
에 의존하고 있다는 사실은 변하지 않습니다Interface
를 생성해 주겠습니다Interface
를 생성하겠습니다Service
레이어에서 중간 다리 역할을 해주고 있으니, port
라는 패키지를 만들어서 그 아래에 생성해 주겠습니다MailSender
가 가져야하는 send
라는 메서드만 정의해 줬습니다public interface MailSender {
void send(String email, String title, String content);
}
MailSender interface
를 구현한 구현체를 생성해줍니다infrastructure
패키지 아래에 생성하겠습니다@Component // component scan 대상인 것을 알려줍니다
@RequiredArgsConstructor
public class MailSenderImpl implements MailSender {
private final JavaMailSender javaMailSender;
@Override
public void send(String email, String title, String content) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email);
message.setSubject(title);
message.setText(content);
javaMailSender.send(message);
}
}
CertificationService
에서 본래 의존하고 있던 JavaMailSender
대신에 우리가 만든 MailSender
를 바라보게 만들어줍니다@Service
@RequiredArgsConstructor
public class CertificationService {
private final MailSender mailSender;
public void send(String email, long userId, String certificationCode) {
String certificationUrl = generateCertificationUrl(userId, certificationCode);
String title = "Please certify your email address";
String content = "Please click the following link to certify your email address: " + certificationUrl;
mailSender.send(email, title, content);
}
private String generateCertificationUrl(long userId, String certificationCode) {
return "http://localhost:8080/api/users/" + userId + "/verify?certificationCode=" + certificationCode;
}
}
CertificationService
에서 MailSender
라는 인터페이스를 구현한 구현체만 주입해주면 됩니다mock
이라는 패키지를 만들고 그 아래에 가짜 MailSender
역할을 수행할 클래스를 생성해주겠습니다public class FakeMailSender implements MailSender {
public String email;
public String title;
public String content;
@Override
public void send(String email, String title, String content) {
this.email = email;
this.title = title;
this.content = content;
}
}
CertificaitonServiceTest
에 사용하도록 하겠습니다public class CertificationServiceTest {
@Test
void 이메일과_컨텐츠가_제대로_만들어져서_보내지는지_테스트한다() {
// Given
FakeMailSender fakeMailSender = new FakeMailSender();
CertificationService certificationService = new CertificationService(fakeMailSender);
// When
certificationService.send("kok202@naver.com", 1, "aaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
// Then
assertThat(fakeMailSender.email).isEqualTo("kok202@naver.com");
assertThat(fakeMailSender.title).isEqualTo("Please certify your email address");
assertThat(fakeMailSender.content).isEqualTo(
"Please click the following link to certify your email address: http://localhost:8080/api/users/1/verify?certificationCode=aaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
);
}
}