Software Architecture
Spring 기반의 Layer별 테스트케이스 작성 가이드
멋진그이름
2024. 4. 3. 19:55
<개요>
- 테스트의 크기는 되도록이면 작게
- 각 테스트 단위는 독립적으로 주입할 수 있도록
- 필요한 것만 주입받고 테스트 해야하며
- 최대한 빠르게 테스트 실행이 가능해야 함
<내용>
- Layer
- Controller
- Web을 통한 호출시 PathVariable, RequestBody, Header, 인증등 을 담당하는 역할
- Service
- Biz Logic 구현
- Biz Transaction 이 필요할 경우
- Repository
- Storage 와 Application 의 다리역할
- 객체변환, 타입검증
- POJO
- 독립된 구현
- 주로 Input / Output 을 통합 로직 수행
- Controller
- 공통사항
- Given / When / Then 으로 작성하면 편함 (준비-실행-검증)
- 각 Layer 별 테스트 코드는 다른 레이어가 정상동작한다는 가정으로 테스트하는 것이 원칙
- Controller Layer
- WebMvc에 관련된 Context만 로딩 (WebMvcTest)
- 사용할 Bean들만 TestConfiguration 으로 정의하여 Context의 가동범위를 최소한 으로 한다.
- Controller Layer
@RunWith(SpringRunner.class)
@WebMvcTest(BizController.class
)
@Import(SecurityConfig.class)
@ContextConfiguration(classes = SpringSecurityWebAuthTestConfig.class)
public class BizControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private BizService bizService;
@Test
@WithUserDetails("admin")
public void getAllData() throws Exception{
List<DataDto> datas = new ArrayList<>();
DataDto dataDto = new DataDto();
dataDto.setDataId(1);
String dataName = "ttt";
dataDto.setDataName(dataName);
dataDto.setUserId(1);
datas.add(dataDto);
Page<DataDto> pages=new PageImpl<>(datas, Pageable.unpaged(), 1);
//given
given(bizService.findAllDatas(PageRequest.of(0,1))).willReturn(pages);
//when , then
this.mvc.perform(get("/datas/all?page=0&size=1"))
.andExpect(jsonPath("content").isNotEmpty())
.andExpect(jsonPath("content[0].dataId").value(1))
.andExpect(status().isOk());
}
-
- 테스트 메소드 작성
- MockBean : Mockup 대상
- given : 테스트 범위내에서 정상동작할 경우의 응답, 혹은 주어진 조건
- when : mvc.perform : 수행
- then : andExpect : 기대값
- 테스트 메소드 작성
- Service Layer
- JUnit으로만 테스트 (Spring Mvc 필요없음)
- 테스트 대상인 Service 만 Inject, 나머지는 Mock
@RunWith(MockitoJUnitRunner.class)
public class BizServiceTest {
@Mock
private BizRepository dataRepository;
@Mock
private ModelMapper modelMapper;
@InjectMocks
private BizService bizService;
@Test
public void createService() throws Exception {
DataDto dataDto = new DataDto();
dataDto.setDataId(1);
dataDto.setUserId(1);
dataDto.setDataName("text");
DataEntity dataEntity = new DataEntity();
dataEntity.setDataId(1);
dataEntity.setUserId(1);
dataEntity.setDataName("text");
//given
given(modelMapper.map(dataDto, DataEntity.class)).willReturn(dataEntity);
given(modelMapper.map(dataEntity, DataDto.class)).willReturn(dataDto);
//when
DataDto result = dataService.createData(dataDto);
//then
Assert.assertEquals(dataDto, result);
}
-
- 테스트 메소드 작성
- given : 테스트 범위내에서 정상동작할 경우의 응답, 혹은 주어진 조건
- when : 테스트 대상
- then : assertEquals ( expected, actual)
- 테스트 메소드 작성
- Repository Layer
- DataJpaTest 관련된 Context만 로딩
- 사용할 Bean들만 정의하여 가동범위 최소한으로
@RunWith(SpringRunner.class) @DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @Import({EnableEncryptablePropertiesConfiguration.class, JasyptConfig.class, SpringSecurityWebAuthTestConfig.class, TestJpaAuditingConfig.class}) public class NewsRepositoryTest { @Autowired private NewsRepository newsRepository; @Test public void findAllByEnabled(){ //given Pageable pageable = PageRequest.of(0,10); //when Page<NewsEntity> newsEntityPage = newsRepository.findAllByEnabled(true, pageable); //then Assert.assertEquals(1, newsEntityPage.getTotalElements()); }
- 테스트 메소드 작성
- given : 테스트 범위내에서 정상동작할 경우의 응답, 혹은 주어진 조건
- when : 테스트 대상
- then : assertEquals ( expected, actual )
- Local Test의 경우 H2 나 기타 메모리DB로 기동될때마다 테스트 데이터를 넣어놓으면 독립적인 테스트가 가능하기 때문에 편리하다.
- 독립 Module
- Context 기동없이 가능
public class EmailValidationTest { @Test public void validation(){ Pattern codePattern = PatternValidator.ValidationType.EMAIL.getMyPattern(); Matcher matcher = codePattern.matcher("terst@gmail.com"); Assert.assertTrue(matcher.matches()); matcher = codePattern.matcher("test-1@naver.com"); Assert.assertTrue(matcher.matches());
- Bean주입없이 그냥 Java new로 POJO 테스트
public class JasyptTest {
@Test
public void encryptDecrypt() throws Exception {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword("");
config.setAlgorithm("PBEWithMD5AndDES");
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
String raw = "abcdefg";
String encrypted = encryptor.encrypt(raw);
String decrypted = encryptor.decrypt(encrypted);
Assert.assertEquals(raw , decrypted);
}
Git Remote Repository에 Push하기전에 Local Test Case를 모두 통과하는지 반드시 확인해야 한다.