ν
μ€νΈλ₯Ό μλνν μλ‘ κ³ νμ§ μννΈμ¨μ΄λ₯Ό λ λΉ λ₯΄κ² μΆμν μ μμ΅λλ€.
ν
μ€νΈ μλλ ν΅ν© ν
μ€νΈλ³΄λ€ λ¨μ ν
μ€νΈκ° λΉ λ₯΄λ―λ‘, κ°λ₯ν ν λ¨μ ν
μ€νΈλ‘ λ€μν μν©μ λ€λ£¨κ³ , ν΅ν© ν
μ€νΈλ κΈ°λ₯ ν
μ€νΈλ μ£Όμ μν©μ μ΄μ μ λ§μΆλ κ²μ΄ μ’μ΅λλ€.
μ½λ 볡μ¬
public class CardNumberValidatorTest {
private WireMockServer wireMockServer;
@BeforeEach
void setUp() {
wireMockServer = new WireMockServer(options().port(8089));
wireMockServer.start();
}
@AfterEach
void tearDown() {
wireMockServer.stop();
}
@Test
void valid() {
wireMockServer.stubFor(post(urlEqualTo("/card"))
.withRequestBody(equalTo("1234567890"))
.willReturn(aResponse()
.withHeader("Content-Type", "text/plain")
.withBody("ok"))
);
CardNumberValidator validator =
new CardNumberValidator("http://localhost:8089");
CardValidity validity = validator.validate("1234567890");
assertEquals(CardValidity.VALID, validity);
}
@Test
void timeout() {
wireMockServer.stubFor(post(urlEqualTo("/card"))
.willReturn(aResponse()
.withFixedDelay(5000))
);
CardNumberValidator validator =
new CardNumberValidator("http://localhost:8089");
CardValidity validity = validator.validate("1234567890");
assertEquals(CardValidity.TIMEOUT, validity);
}
}
μ½λ 볡μ¬
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserApiE2ETest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void weakPwResponse() {
String reqBody = "{\"id\": \"id\", \"pw\": \"123\", \"email\": \"a@a.com\" }";
RequestEntity<String> request =
RequestEntity.post(URI.create("/users"))
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(reqBody);
ResponseEntity<String> response = restTemplate.exchange(
request,
String.class);
assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
assertTrue(response.getBody().contains("WeakPasswordException"));
}
}
`
ν μ€νΈ μ£Όλ κ°λ°μ ν΅ν΄ μμ±λ ν μ€νΈ μ½λλ CI/CD νμ΄νλΌμΈμμ μλν ν μ€νΈλ‘ μ¬μ©λ©λλ€. μ΄λ₯Ό ν΅ν΄ λ°°ν¬ μ μ λ²κ·Έλ₯Ό λ°κ²¬νκ³ λ°©μ§νμ¬ μννΈμ¨μ΄ νμ§μ΄ μ νλλ κ²μ λ§μ μ μμ΅λλ€. κ·Έλ¬λ ν μ€νΈ μ½λλ μ ν μ½λμ λ§μ°¬κ°μ§λ‘ μ§μμ μΈ μ μ§λ³΄μκ° νμν©λλ€. ν μ€νΈ μ½λλ₯Ό λ°©μΉνλ©΄ μ¬λ¬ λ¬Έμ κ° λ°μν μ μμ΅λλ€.
μ½λλ₯Ό λ³κ²½ν λ κΈ°μ‘΄ κΈ°λ₯μ΄ μ¬λ°λ₯΄κ² λμνλμ§ νμΈνλ νκ· ν μ€νΈλ₯Ό μλνν©λλ€. νμ§λ§ κΉ¨μ§ ν μ€νΈλ₯Ό λ°©μΉνλ©΄ νκ· ν μ€νΈμ κ²μ¦ λ²μκ° μ€μ΄λ€μ΄ μλ‘μ΄ λ³κ²½ μ¬νμ΄ κΈ°μ‘΄ κΈ°λ₯μ λ―ΈμΉλ μν₯μ μ νν νμ νκΈ° μ΄λ €μμ§λλ€.
κΉ¨μ§ ν μ€νΈκ° λ§μμ§λ©΄, μ΄λ¬ν ν μ€νΈλ₯Ό ν΅κ³Όμν€κΈ° μν΄ λ§μ λ Έλ ₯μ΄ νμν΄μ§λλ€. μ΄λ TDDμ λ³Έλ λͺ©μ μμ λ²μ΄λ μ μμ΅λλ€.
μ΄λ¬ν μ μνμ λ°©μ§νλ €λ©΄ ν μ€νΈ μ½λμ μ μ§λ³΄μμ±μ΄ λμμΌ ν©λλ€.
"κΉ¨μ§ μ 리창 μ΄λ‘ "μ μ¬μν 무μ§μλ₯Ό λ°©μΉνλ©΄ λ ν° λ¬Έμ λ‘ μ΄μ΄μ§ κ°λ₯μ±μ΄ λμμ§λ€λ μ΄λ‘ μ λλ€. μ΄λ ν μ€νΈ μ½λμλ μ μ©λ©λλ€. ν μ€νΈ μ½λλ₯Ό λ°©μΉνκΈ° μμνλ©΄ μ€ν¨νλ ν μ€νΈκ° μ μ λ§μμ Έ, κ²°κ΅μλ ν μ€νΈλ₯Ό μ μ§λ³΄μν μ μμ μ λλ‘ μ νλ μ μμ΅λλ€.
@Test
void dateFormat() {
LocalDate date = LocalDate.of(1945,8,15);
String dataStr = formatDate(date);
assertEquals(date.getYear()+"λ
"+
date.getMonthValue()+"μ "+
date.getDayOfMonth()+"μΌ ", dateStr);
}
ν μ€νΈ μ½λμμ κ°μ²΄λ₯Ό μμ±ν λ μ¬μ©ν κ°μ νμΈνκΈ° μν΄ νλλ λ³μλ₯Ό μ°Έμ‘°νμ§ μλλ‘ ν΄μΌ ν©λλ€.
@Test
void dateFormat() {
LocalDate date = LocalDate.of(1945,8,15);
String dataStr = formatDate(date);
assertEquals("1945λ
8μ 15μΌ", dateStr);
}
μ΄λ κ² νλ©΄ ν μ€νΈ μ½λκ° λ λͺ νν΄μ§κ³ μ μ§λ³΄μνκΈ° μ¬μμ§λλ€.
private List<Integer> answers = Arrays.asList(1,2,3,4);
private Long respondentId = 100L;
@DisplayName("λ΅λ³μ μ±κ³΅νλ©΄ κ²°κ³Ό μ μ₯ν¨")
@Test
public void saveAnswerSuccessfully() {
// λ΅λ³ν μ€λ¬Έμ΄ μ‘΄μ¬
Survey survey = SurveyFactory.createApprovedSurvey(1);
surveyRepository.save(survey);
// μ€λ¬Έ λ΅λ³
SurveyAnswerRequest surveyAnswer = SurveyAnswerRequest.builder()
.surveyId(survey.getId())
.respondentId(respondentId)
.answers(answers)
.build();
svc.answerSurvey(surveyAnswer);
// μ μ₯ κ²°κ³Ό νμΈ
SurveyAnswer savedAnswer =
memoryRepository.findBySurveyAndRespondent(
survey.getId(), respondentId);
assertAll(
()->assertEquals(respondentId, savedAnswer.getRespondentId()),
()->assertEquals(answer.size(), savedAnswer.getAnswers().size()),
()->assertEquals(answer.get(0), savedAnswer.getAnswers().get(0)),
()->assertEquals(answer.get(1), savedAnswer.getAnswers().get(1)),
()->assertEquals(answer.get(2), savedAnswer.getAnswers().get(2)),
()->assertEquals(answer.get(3), savedAnswer.getAnswers().get(3))
);
}
λ¨μΈκ³Ό κ°μ²΄ μμ±μ νλμ λ³μλ₯Ό μ¬μ©ν κ²½μ°, νλμ κ°μ κ²μ¦νλ κ³Όμ μμ μ€ν¨νλ©΄ ν΄λΉ νλλ₯Ό νμΈν΄μΌ νλ€.
λλ ν
μ€νΈ μ½λλ₯Ό μ²μ 보λ μ¬λμ λ³μμ νλλ₯Ό μ€κ°λ©° ν
μ€νΈ μ½λλ₯Ό μ΄ν΄ν΄μΌ νλ€.
@DisplayName("λ΅λ³μ μ±κ³΅νλ©΄ κ²°κ³Ό μ μ₯ν¨")
@Test
public void saveAnswerSuccessfully() {
// λ΅λ³ν μ€λ¬Έμ΄ μ‘΄μ¬
Survey survey = SurveyFactory.createApprovedSurvey(1);
surveyRepository.save(survey);
// μ€λ¬Έ λ΅λ³
SurveyAnswerRequest surveyAnswer = SurveyAnswerRequest.builder()
.surveyId(1L)
.respondentId(100L)
.answers(Arrays.asList(1,2,3,4))
.build();
svc.answerSurvey(surveyAnswer);
// μ μ₯ κ²°κ³Ό νμΈ
SurveyAnswer savedAnswer =
memoryRepository.findBySurveyAndRespondent(
survey.getId(), respondentId);
assertAll(
()->assertEquals(100L, savedAnswer.getRespondentId()),
()->assertEquals(4, savedAnswer.getAnswers().size()),
()->assertEquals(1, savedAnswer.getAnswers().get(0)),
()->assertEquals(2, savedAnswer.getAnswers().get(1)),
()->assertEquals(3, savedAnswer.getAnswers().get(2)),
()->assertEquals(4, savedAnswer.getAnswers().get(3))
);
}
κ°μ²΄ μμ±κ³Ό λ¨μΈμμ λ³μ λμ κ°μ μ¬μ©νλ©΄ μ½λ κ°λ μ±μ΄ μ’μμ Έμ ν μ€νΈ μ½λλ₯Ό μ½κ² νμ ν μ μλ€.
@DisplayName("κ°μ IDκ° μμΌλ©΄ κ°μ
μ μ±κ³΅νκ³ λ©μΌμ μ μ‘ν¨")
@Test
void registerAndSendEmail() {
userRegister.register("id", "pw", "email");
// κ²μ¦1 : νμ λ°μ΄ν°κ° μ¬λ°λ₯΄κ² μ μ₯λμλμ§ κ²μ¦
User savedUser = userRepository.findById("id");
assertEquals("id", savedUser.getId());
assertEquals("email", savedUser.getEmail());
// κ²μ¦2 : μ΄λ©μΌ λ°μ‘μ μμ²νλμ§ κ²μ¦
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
BDDMockito.then(mockEmailNotifier)
.should().sendRegisterEmail(captor.capture());
String realEmail = captor.getValue();
assertEquals("email@email.com", realEmail);
}
ν ν μ€νΈ λ©μλμμ μλ‘ λ€λ₯Έ λ΄μ©μ κ²μ¦νλ κ²μ μ§μν΄μΌ ν©λλ€. μλ₯Ό λ€μ΄, νμ λ°μ΄ν° μ μ₯ μ¬λΆμ μ΄λ©μΌ λ°μ‘ μμ² μ¬λΆλ₯Ό λμμ κ²μ¦νλ λμ , κ°κ°μ λ΄μ©μ λ³λμ ν μ€νΈλ‘ λΆλ¦¬ν΄ μ§μ€λλ₯Ό λμ¬μΌ ν©λλ€.
@DisplayName("κ°μ IDκ° μμΌλ©΄ κ°μ
μ±κ³΅ν¨")
@Test
void noDupId_RegisterSuccess() {
userRegister.register("id", "pw", "email");
User savedUser = fakeRepository.findById("id");
assertEquals("id", savedUser.getId());
assertEquals("email", savedUser.getEmail());
}
@DisplayName("κ°μ
νλ©΄ λ©μΌμ μ μ‘ν¨")
@Test
void whenRegisterThenSendMail() {
userRegister.register("id", "pw", "email@email.com");
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
then(mockEmailNotifier).should().sendRegisterEmail(captor.capture());
String realEmail = captor.getValue();
assertEquals("email@email.com", realEmail);
}
@DisplayName("μ½ν μνΈλ©΄ κ°μ
μ€ν¨")
@Test
void weakPassword() {
BDDMockito.given(mockPasswordChecker.checkPasswordWeak("pw"))
.willReturn(true);
assertThrows(WeakPasswordException.class, ()->{
userRegister.register("id", "pw", "email");
});
}
μ νν μΌμΉνλ μν©μ μ μν λͺ¨μ(Mock) κ°μ²΄λ μμ λ³νμλ ν
μ€νΈκ° μ€ν¨ν μ μμ΅λλ€.
μ½ν μνΈ ν
μ€νΈλ UserRegister κ° μνλ λλ‘ λμνλ μ§ νμΈνκΈ° μν μ½λμ
λλ€.
μ΄λ΄ κ²½μ° μμμ κ°μ μΌμΉνλλ‘ λͺ¨μ κ°μ²΄λ₯Ό μ€μ νλ κ²μ΄ μ’μ΅λλ€. μλ₯Ό λ€μ΄, Mockito.anyString()μ μ¬μ©ν΄ μμμ λ¬Έμμ΄κ³Ό μΌμΉνλλ‘ μ€μ ν©λλ€.
@DisplayName("μ½ν μνΈλ©΄ κ°μ
μ€ν¨")
@Test
void weakPassword() {
BDDMockito.given(mockPasswordChecker.checkPasswordWeak(Mockito.anyString()))
.willReturn(true);
assertThrows(WeakPasswordException.class, ()->{
userRegister.register("id", "pw", "email");
});
}
λͺ¨μ κ°μ²΄λ₯Ό νΈμΆνλμ§ μ¬λΆλ₯Ό νμΈνλ κ²½μ°λ λμΌν©λλ€.
@DisplayName("νμ κ°μ
μ μνΈ κ²μ¬ μνλ¨")
@Test
void checkPassword() {
userRegister.register("id", "pw", "email");
BDDMockito.then(mockPasswordChecker)
.should()
.checkPasswordWeak("pw");
}
@DisplayName("νμ κ°μ
μ μνΈ κ²μ¬ μνλ¨")
@Test
void checkPassword() {
userRegister.register("id", "pw", "email");
// PasswordChecker#checkPasswordWeak() λ©μλ νΈμΆ μ¬λΆ κ²μ¬
BDDMockito.then(mockPasswordChecker)
.should()
.checkPasswordWeak(Mockito.anyString());
// UserRepository#findById() λ©μλλ₯Ό νΈμΆνμ§ μλ κ²μ κ²μ¬
BDDMockito.then(mockRepository)
.should(Mockito.never())
.findById(Mockito.anyString());
}
λ΄λΆ ꡬνμ κ²μ¦νλ κ²μ μ’μ λ°©λ²μ΄ μλλλ€. λ΄λΆ ꡬνμ μΈμ λ μ§ λ°λ μ μμΌλ―λ‘, μ€ν κ²°κ³Όλ₯Ό κ²μ¦νλ λ° μ§μ€ν΄μΌ ν©λλ€.
μμ μ½λμ κ²½μ° register()
λ©μλκ° PasswordChecker#checkPasswordWeak() λ©μλλ₯Ό νΈμΆνλμ§ κ²μ¦νλ κ²λ³΄λ€ μ½ν μνΈμΌ λ registerμ κ²°κ³Όκ° μ¬λ°λ₯Έμ§ κ²μ¦ν΄μΌν©λλ€.
μ΄λ―Έ μ‘΄μ¬νλ μ½λμ λ¨μ ν
μ€νΈλ₯Ό μΆκ°νλ©΄ μ΄μ© μ μμ΄ λ΄λΆ ꡬνμ κ²μ¦ν΄μΌ ν λλ μμ΅λλ€. μλ₯Ό λ€μ΄ λ κ±°μ μ½λμ changeEmail()
λ©μλλ μ΄λ©μΌμ λ³κ²½νλ κΈ°λ₯μ ꡬννκ³ μμ΅λλ€.
public void changeEmail(String id, String email) {
int cnt = userDao.countById(id);
if(cnt == 0) {
throw new NoUserException();
}
userDao.updateEmail(id, email);
}
λ κ±°μ μ½λμμ DAOλ λ€μν update, select λ±μ λ©μλλ₯Ό μ μνκ³ μλ κ²½μ°κ° λ§μ΅λλ€.
λ°λΌμ λ©λͺ¨λ¦¬λ₯Ό μ΄μ©ν κ°μ§ ꡬνμΌλ‘ λ체νκΈ°κ° μ½μ§ μμ΅λλ€. μ΄λ° κ²½μ° λͺ¨μ κ°μ²΄λ₯Ό λ§μ΄ νμ©ν©λλ€.
@Test
void changeEmailSuccessfully() {
given(mockDao.countById(Mockito.anyString())).willReturn();
emailService.changeEmail("id", "new@somehost.com");
then(mockDao).should()
.updateEmail(Mockito.anyString(), Mockito.matches("new@somehost.com");
}
μ΄ μ½λλ μ΄λ©μΌμ μμ νλμ§ νμΈνκΈ° μν΄ updateEmail() λ©μλ νΈμΆ μ¬λΆλ₯Ό νμΈν©λλ€.
κΈ°λ₯μ΄ μ μμ μΌλ‘ λμνλμ§ νμΈν μλ¨μ΄ ꡬν κ²μ¦λ°μ μλ€λ©΄ λͺ¨μ κ°μ²΄λ₯Ό μ¬μ©ν΄μ ν μ€νΈ μ½λλ₯Ό μμ±ν΄μΌ νμ§λ§ μΌλ¨ ν μ€νΈ μ½λλ₯Ό μμ±ν λ€μλ μ μ§μ μΌλ‘ μ½λλ₯Ό 리ν©ν λ§ν΄μ ꡬνμ΄ μλ κ²°κ³Όλ₯Ό κ²μ¦ν μ μλλ‘ μλν΄μΌ ν©λλ€.
@BeforeEach
void setUp() {
changeService = new ChangeUserService(memoryRepository);
memoryRepository.save(
new User("id", "name", "pw", new Address("μμΈ", "λΆλΆ")
);
}
@Test
void noUser() {
assertThrows(
UserNotFoundException.class,
()-> changeService.ChangeAddress("id2", new Address("μμΈ", "λ¨λΆ"))
);
}
@Test
void changeAddress() {
changeService.changeAddress("id", new Address("μμΈ", "λ¨λΆ"));
User user = memoryRepository.findById("id");
assertEquals("μμΈ", user.getAddress().getCity());
}
@Test
void changePw() {
changeService.changePw("id", "pw", "newpw");
User user = memoryRepository.findById("id");
assertTrue(user.matchPassword("newpw"));
}
@Test
void pwNotMatch() {
assertThrows(
IdPwNotMatchException.class,
()->changeService.changePw("id", "pw2", "newpw")
);
}
μ€λ³΅λ μ½λλ₯Ό μ κ±°νκΈ° μν΄ @BeforeEach κ°μ λ©μλλ₯Ό μ΄μ©ν μ μμ§λ§, μ΄λ ν μ€νΈλ₯Ό κΉ¨μ§κΈ° μ¬μ΄ κ΅¬μ‘°λ‘ λ§λ€ μ μμ΅λλ€. ν μ€νΈ λ©μλλ λ 립μ μΈ νλ‘κ·Έλ¨μΌλ‘μ, μν© κ΅¬μ± μ½λκ° λ©μλ λ΄μ μμ΄μΌ μ체μ μΌλ‘ μΆ©λΆν μ€λͺ λ μ μμ΅λλ€.
@BeforeEach
void setUp() {
changeService = new ChangeUserService(memoryRepository);
memoryRepository.save(
new User("id", "name", "pw2", new Address("μμΈ", "λΆλΆ")
);
}
@SpringBootTest
@Sql("classpath:init-data.sql")
public class UserRegisterIntTestUsingSql {
@Autowired
private UserRegister register;
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
void λμΌIDκ°_μ΄λ―Έ_μ‘΄μ¬νλ©΄_μ΅μ
μ
() {
// μ€ν κ²°κ³Ό νμΈ
assertThrows(DupIdException.class,
()->register.register("cbk", "strongpw", "email@email.com")
);
}
@Test
void μ‘΄μ¬νμ§_μμΌλ©΄_μ μ₯ν¨() {
// μ€ν
register.register("cbk2", "strongpw", "email@email.com");
// ...
}
}
μ
μ
μ μ΄μ©ν μν© μ€μ κ³Ό λΉμ·ν κ²μΌλ‘ ν΅ν© ν
μ€νΈμ DB λ°μ΄ν° μ΄κΈ°νκ° μμ΅λλ€.
DB μ°λμ ν¬ν¨ν ν΅ν© ν
μ€νΈλ₯Ό μ€ννλ €λ©΄ DB λ°μ΄ν°λ₯Ό μλ§κ² ꡬμ±ν΄μΌ ν©λλ€.
μ€νλ§ νλ μμν¬μμλ @Sql
μ λ
Έν
μ΄μ
μ μ¬μ©νμ¬ ν
μ€νΈλ₯Ό μ€ννκΈ° μ νΉμ 쿼리λ₯Ό μ€νν μ μμ΅λλ€.
@Sql μ λ Έν μ΄μ μΌλ‘ μ§μ ν sql νμΌμ λ€μκ³Ό κ°μ΄ ν μ€νΈμ νμν λ°μ΄ν°λ₯Ό μ΄κΈ°νν©λλ€.
truncate table user;
insert into user values ('cbk', 'pw', 'cbk@cbk.com');
insert into user values ('tddhit', 'pw1', 'tddhit@ilovetdd.com')
μ΄ μΏΌλ¦¬λ μ¬λ¬ ν μ€νΈκ° λμΌν λ°μ΄ν°λ₯Ό μ¬μ©ν μ μκ² λ§λ€μ΄μ£Όμ΄μ ν΅ν© ν μ€νΈ λ©μλλ λ°μ΄ν° μ΄κΈ°νλ₯Ό μν μ½λλ₯Ό μμ±νμ§ μμλ λ©λλ€. μ΄ λ°©μμ νΈλ¦¬νμ§λ§ μ μ λ©μλμ λ§μ°¬κ°μ§λ‘ 쿼리 νμΌμ μ‘°κΈλ§ λ³κ²½ν΄λ ν μ€νΈκ° κΉ¨μ§ μ μκ³ κ²°κ΅ μ μ§λ³΄μλ₯Ό μ΄λ ΅κ² λ§λλλ€.
ν΅ν© ν μ€νΈ μ½λλ₯Ό λ§λ€ λλ λ€μ λ κ°μ§λ‘ μ΄κΈ°ν λ°μ΄ν°λ₯Ό λλ μ μκ°ν΄μΌ ν©λλ€.
μ½λκ° λ°μ΄ν°λ κ±°μ λ°λμ§ μκΈ° λλ¬Έμ λͺ¨λ ν μ€νΈκ° λμΌν μ½λκ° λ°μ΄ν°λ₯Ό μ¬μ©ν΄λ λ¬Έμ κ° μμ΅λλ€.
λ°λ©΄μ νΉμ ν
μ€νΈ λ©μλμλ§ μλ―Έ μλ λ°μ΄ν°λ λͺ¨λ ν
μ€νΈκ° 곡μ ν νμκ° μμ΅λλ€.
μ΄λ° λ°μ΄ν°λ νΉμ ν
μ€νΈμμλ§ μμ±ν΄μ ν
μ€νΈ μ½λκ° μμ ν νλκ° λλλ‘ ν΄μΌ ν©λλ€.
@Test
void dupId() {
// μν©
jdbcTemplate.update(
"insert into user values(?,?,?) "+
"on duplicate key update password = ?, email = ?",
"cbk", "pw", "cbk@cbk.com", "pw", "cbk@cbk.com");
// μ€ν κ²°κ³Ό νμΈ
assertThrows(DupIdException.class,
()->register.register("cbk", "strongpw", "email@email.com")
);
}
ν΅ν© ν μ€νΈμμ μ΄κΈ°ν λ°μ΄ν°λ₯Ό λλ λ, λͺ¨λ ν μ€νΈκ° κ°μ κ°μ μ¬μ©νλ λ°μ΄ν°μ ν μ€νΈ λ©μλμμλ§ νμν λ°μ΄ν°λ₯Ό ꡬλΆν΄μΌ ν©λλ€.
ν΅ν© ν
μ€νΈμμ λ°μ΄ν° 곡μ μ£ΌμνκΈ°
μ²λΌ μ€λ³΅ IDλ₯Ό κ°μ§ νμμ΄ μ‘΄μ¬νλ μν©μ λ§λ€κΈ° μν΄ νμν νμ λ°μ΄ν°λ₯Ό μμ±νμ΅λλ€. λΉμ·νκ² μ΄λ©μΌ μμ κΈ°λ₯μ ν
μ€νΈν λμλ μ μ¬ν 쿼리λ₯΄ μ€νν΄μΌ ν©λλ€. κ° ν
μ€νΈ λ©μλμμ μν©μ μ§μ ꡬμ±ν¨μΌλ‘μ¨ ν
μ€νΈ λ©μλλ₯Ό λΆμνκΈ°λ μ’μ§λ§ λ°λλ‘ μν©μ λ§λ€κΈ° μν μ½λκ° μ¬λ¬ ν
μ€νΈ μ½λμ μ€λ³΅λ©λλ€.
ν
μ΄λΈ μ΄λ¦μ΄λ μΉΌλΌ μ΄λ¦μ΄ λ°λλ©΄ μ¬λ¬ ν
μ€νΈ λ©μλλ₯Ό μμ ν΄μΌ νλ―λ‘ μ μ§λ³΄μμ μ’μ§ μμ΅λλ€.
ν μ€νΈ λ©μλμμ μ§μ μν©μ ꡬμ±νλ©΄μ μ½λ μ€λ³΅μ μμ λ λ°©λ²μ΄ μλλ°, μν© μ€μ μ μν 보쑰 ν΄λμ€λ₯Ό μ¬μ©νλ κ²μ λλ€.
public class UserGivenHelper {
private JdbcTemplate jdbcTemplate;
public UserGivenHelper(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void givenUser(String id, String pw, String email) {
jdbcTemplate.update(
"insert into user values (?,?,?) "+
"on duplicate key update password = ?, email = ?",
id, pw, email, pw, email);
}
}
μν©μ ꡬμ±νκΈ° μν 보쑰 ν΄λμ€λ₯Ό μ¬μ©νκ²λλ©΄ μλμ κ°μ΅λλ€.
Autowired JdbcTemplate jdbcTemplate;
private UserGivenHelper given;
@BeforeEach
void setUp() {
given = new UserGivenHelper(jdbcTemplate);
}
@Test
void dupId() {
given.givenUser("cbk", "pw", "cbk@cbk.com");
// μ€ν κ²°κ³Ό νμΈ
assertThrows(DupIdException.class,
()->register.register("cbk", "strongpw", "email@email.com")
);
}
ν΅ν© ν μ€νΈμμ λμΌν μν© μ€μ μ΄λ κ²μ¦μ λ°λ³΅ν λλ 보쑰 ν΄λμ€λ₯Ό μ¬μ©νμ¬ μ μ§λ³΄μλ₯Ό μ©μ΄νκ² ν μ μμ΅λλ€. μ΄λ₯Ό ν΅ν΄ μ€λ³΅λ μ½λλ₯Ό μ€μ΄κ³ ν μ€νΈ μ½λμ κ°λ μ±κ³Ό μ μ§λ³΄μμ±μ λμΌ μ μμ΅λλ€.
κ°μ ν
μ€νΈ λ©μλκ° μ€ν νκ²½μ λ°λΌ μ±κ³΅νκ±°λ μ€ν¨νλ©΄ μ λ©λλ€.
λ‘컬 κ°λ°νκ²½, λΉλ μλ², λ§₯ OS λ± μ¬λ¬ νκ²½μμ λμΌνκ² λμν΄μΌ ν©λλ€.
μ€ν νκ²½μ λ°λΌ λ¬Έμ κ° λλ μλ‘ νμΌ κ²½λ‘κ° μμ΅λλ€.
public class BulkLoaderTest {
private String bulkFilePath = "d:\\mywork\\temp\\bulk.txt";
@Test
void load() {
BulkLoader loader = new BulkLoader();
loader.load(bulkFilePath);
// ...
}
}
μ΄λ΄ κ²½μ° μλ κ²½λ‘λ₯Ό μ¬μ©ν΄μΌ ν©λλ€.
public class BulkLoaderTest {
private String bulkFilePath = "src/test/resources/bulk.txt";
@Test
void load() {
BulkLoader loader = new BulkLoader();
loader.load(bulkFilePath);
// ...
}
}
ν μ€νΈ μ½λμμ νμΌμ μμ±νλ κ²½μ°μλ νΉμ OSλ κ°λ° νκ²½μλ°λΌ μ¬λ°λ₯΄κ² λμνλλ‘ μ£Όμν΄μΌ ν©λλ€.
public class ExporterTest {
@Test
void export() {
String folder = System.getProperty("java.io.tempdir");
Exporter exporter = new Exporter(folder);
exporter.export("file.txt");
Path path = Paths.get(folder, "file.txt");
assertTrue(Files.exists(path));
}
}
μ΄ μ½λλ μ€ν νκ²½μ μλ§μ μμ ν΄λ κ²½λ‘λ₯Ό ꡬν΄μ λμν©λλ€.
κ°νΉ νΉμ OS νκ²½μμλ§ μ€νν΄μΌ νλ ν
μ€νΈλ μμ΅λλ€.
JUnit 5κ° μ 곡νλ @EnabledOnOs
, @DisabledOnOs
λ₯Ό μ΄μ©νλ©΄ λ©λλ€.
νμμ λ§λ£ μ¬λΆλ₯Ό νμΈνλ ν μ€νΈκ²½μ° μ½λ μμ± μμ μλ μ¬μ©ν μ μμ§λ§ μ΄ν μκ°μ΄ μ§λλ©΄μ μ€ν¨νλ ν μ€νΈκ° λ©λλ€.
public class Member {
private LocalDateTime expiryDate;
public boolean isExpired() {
return expiryDate.isBefore(LocalDateTime.now());
}
}
@Test
void notExpired() {
// ν
μ€νΈ μ½λ μμ± μμ 2019.1.1
LocalDateTime expiry = LocalDateTime.of(2019,12,31,0,0,0);
Member m = Member.builder().expiryDate(expiry).build();
assertFalse(m.isExpired());
}
μ΄λ΄ κ²½μ° ν μ€νΈ μ½λμμ λͺ μμ μΌλ‘ μκ°μ μ μ΄ν λ°©λ²μ μ ννλ κ²μ΄ μ’μ΅λλ€.
public class Member {
private LocalDateTime expiryDate;
public boolean passedExpiryDate(LocalDateTime time) {
return expiryDate.isBefore(time);
}
}
@Test
void notExpired() {
LocalDateTime expiry = LocalDateTime.of(2019,12,31,0,0,0);
Member m = Member.builder().expiryDate(expiry).build();
assertFalse(m.passedExpiryDate(LocalDateTime.of(2019,12,30,0,0,0));
}
λ€λ₯Έ λ°©λ²μΌλ‘λ λ³λμ μκ° ν΄λμ€λ₯Ό μμ±νλ κ²μ λλ€.
public class BizClock {
private static BizClock DEFAULT = new BizClock();
private static BizClock instance = DEFAULT;
public static void reset() {
instance = DEFAULT;
}
public static LocalDateTime now() {
return instance.timeNow();
}
protected void setInstance(BizClock bizClock) {
BizClock.instance = bizClock;
}
public LocalDateTime timeNow() {
return LocalDateTime.now();
}
}
public class Member {
private LocalDateTime expiryDate;
public boolean isExpired() {
return expiryDate.isBefore(BizClock.now());
}
BizClock
ν΄λμ€μ setInstance() λ©μλλ₯Ό μ¬μ©νλ©΄ instance μ μ νλλ₯Ό κ΅μ²΄ν μ μμΌλ―λ‘ ν΄λμ€λ₯Ό μμλ°μ νμ ν΄λμ€λ₯Ό μ΄μ©νλ©΄ BizClock.now()μμ νμ¬ μκ°μ ꡬν μ μμ΅λλ€.
class TestBizClock extends BizClock {
private LocalDateTime now;
public TestBizClock() {
setInstance(this);
}
public void setNow(LocalDateTime now) {
this.now = now;
}
@Override
public LocalDateTime timeNow() {
return now != null ? now : super.now();
}
}
public class MemberTest {
TestBizClock testClock = new TestBizClock();
@AfterEach
void resetClock() {
BizClock.reset();
}
@Test
void notExpired() {
testClock.setNow(LocalDateTime.of(2019,1,1,13,0,0));
LocalDateTime expiry = LocalDateTime.of(2019,12,31,0,0,0);
Member m = Member.builder().expiryDate(expiry).build();
assertFalse(m.isExpired());
}
ν μ€νΈ μ½λλ λ¨μν κ²μ¦ λκ΅¬κ° μλλΌ, μννΈμ¨μ΄μ νμ§μ μ μ§νκ³ κ°μ νλ μ€μν μμ°μ λλ€. λ°λΌμ ν μ€νΈ μ½λμ μ μ§λ³΄μμ±μ λμ΄λ κ²μ νμμ μ΄λ©°, μ΄λ₯Ό μν΄ μμμ μΈκΈν λ°©λ²λ€μ μ€μ²νλ κ²μ΄ μ’μ΅λλ€.