일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- LV0
- 네트워크
- LV03
- 프로그래머스
- 알고리즘
- LV.02
- 배열
- CoffiesVol.02
- docker
- 일정관리프로젝트
- JPA
- SQL
- Redis
- 데이터 베이스
- Lv.0
- LV02
- Til
- spring boot
- GIT
- LV01
- LV1
- S3
- 이것이 자바다
- Java
- Join
- 포트폴리오
- 연습문제
- mysql
- 코테
- 디자인 패턴
- Today
- Total
코드 저장소.
JUnit & Mockito 기반 Spring 단위 테스트 코드 작성 본문
목차
1. 단위 테스트
- 단위 테스트의 필요성
2.Mockito
- Mock객체는 무엇인가?
- Mickto + Junit5 기반의 테스트 코드
- 컨트롤러 계층 단위 테스트
- 서비스 계층 단위 테스트
- 리포지터리 계층 단위 테스트
1.단위 테스트
- 단위 테스트의 필요성
우선 단위 테스트는 응용 프로그램에서 테스트 가능한 가장 작은 소프트웨어를 실행하여 예상대로 동작하는지 확인하는 테스트를 말한다. 즉, 하나의 기능이 올바르게 동작하는지를 독립적으로 테스트하는 것을 단위 테스트를 말한다.
그러므로 단위 테스트는 테스트하고자 하는 부분만 독립적으로 테스트를 하기 때문에 해당 단위를 유지 보수 또는 리팩토링 하더라도 빠르게 문제 여부를 확인할 수 있다.
2.Mockito
1.Mock객체는 무엇인가?
우선 Mockito를 설명하기 전에 Mock객체가 무엇인지를 알아야 할 필요가 있습니다.
우선 Mock객체는 객체와 비슷하게 동작하지만 프로그래머가 직접 그 객체의 행동을 관리하는 객체를 말하고
위키에서는 Mock객체를 다음과 같이 정의하고 있다.
모의 객체(Mock Object)란 주로 객체 지향 프로그래밍으로 개발한 프로그램을 테스트 할 경우 테스트를 수행할 모듈과 연결되는 외부의 다른 서비스나 모듈들을 실제 사용하는 모듈을 사용하지 않고 실제의 모듈을 "흉내"내는 "가짜" 모듈을 작성하여 테스트의 효용성을 높이는 데 사용하는 객체이다. 사용자 인터페이스(UI)나 데이터베이스 테스트 등과 같이 자동화된 테스트를 수행하기 어려운 때 널리 사용된다.
[출저] https://ko.wikipedia.org/wiki/%EB%AA%A8%EC%9D%98_%EA%B0%9D%EC%B2%B4
모의 객체 - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전. -->
ko.wikipedia.org
개념은 이러하고 우선은 아래의 예제를 통해서 설명을 하도록 하겠습니다.
@Test
@DisplayName("게시글 목록")
public void boardList(){
//디비에 저장된 게시글을 전부 조회
}
위에서 쓰인 코드와 같이 DB에서 테이블을 읽고 저장된 게시글을 매번 확인을 하면 디비에도 부담이 가고 복잡한 로직의 경우라면 시간이 매번 오래 걸리게 될 것입니다. 그래서 디비에 저장되어 있는 글을 매번 조회하기보다는 가짜 더미 데이터를 만들어서 테스트를 하게 되면 테스트를 하는 시간도 줄 것이고 불필요한 리소스 또한 막을 수가 있다는 점에서 보면
테스트가 굉장히 필요한 부분이라고 생각됩니다. 그럼 Mockito로 테스트 코드를 작성하는 방법에 대해 알아보겠습니다.
2.Mockito + Junit5기반의 테스트 코드
Mockito는, 개발자가 동작을 직접적으로 제어할 수 있는 가짜 객체를 지원하는 테스트 프레임웍이다. Spring 애플리케이션은 여러 객체들 간의 의존성이 생기는데 이러한 의존성을 모키토를 이용하여 단절 시킴으로 단위 테스트를 쉽게 작성하는 것을 도와준다.
예제로 위의 컨트롤러와 서비스 코드를 예시로 해서 테스트 코드를 작성해 보겠습니다.
@Api(tags = "Member api",value = "회원 관련 api 컨트롤러")
@Log4j2
@RestController
@AllArgsConstructor
@RequestMapping("/api/member")
public class MemberApiController {
private final MemberService memberService;
@Operation(summary = "회원 목록 api",description = "회원전체 목록을 출력한다.")
@GetMapping(path = "/list")
public CommonResponse<Page<MemberResponse>> memberList(@ApiIgnore @PageableDefault(sort = "id",direction = Sort.Direction.DESC,size = 5) Pageable pageable){
Page<MemberResponse> list = null;
try{
list = memberService.findAll(pageable);
}catch (Exception e){
e.printStackTrace();
}
return new CommonResponse<>(HttpStatus.OK.value(),list);
}
.......
}
@Service
@AllArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final RedisTemplate<String,Object> redisTemplate;
/**
* 회원 목록
*
**/
@Transactional(readOnly = true)
public Page<MemberResponse> findAll(Pageable pageable){
Page<Member>list = memberRepository.findAll(pageable);
return list.map(member->new MemberResponse(member));
}
.....
}
컨트롤러 계층 테스트 코드
컨트롤러의 경우에는 사용자가 보낸 요청을 서비스에 전달을 하는 부분이므로 요청 데이터를 서비스에 전달해주고, 서비스가 반환하는 결과를 클라이언트에게 반환하는 부분이다. 보통 컨트롤러를 테스트를 할 때에는@WebMvcTest를 사용합니다.
@SpringBootTest
@AutoConfigureMockMvc
public class MemberApiControllerTest {
.....
@DisplayName("회원 목록")
@Test
public void memberListTest()throws Exception{
given(memberRepository.findAll(any(Pageable.class))).willReturn(Page.empty());
when(memberService.findAll(any(Pageable.class))).thenReturn(Page.empty());
mvc.perform(get("/api/member/list")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print());
verify(memberService,atLeastOnce()).findAll(any());
}
}
서비스 계층 테스트 코드
서비스 테스트는 개발자가 작성한 비지니스 로직이 제대로 작동이 되는지 그리고 로직에 있는 예외처리가 제대로 나오는지를 확인하는것이 목표이다.
@SpringBootTest
@AutoConfigureMockMvc
public class MemberServiceTest {
@InjectMocks
private MemberService memberService;
@Mock
private MemberRepository memberRepository;
@Mock
private BCryptPasswordEncoder bCryptPasswordEncoder;
Member member;
MemberRequest memberRequest;
MemberResponse memberResponse;
SearchType searchType;
@BeforeEach
public void init(){
member = MemberFactory.memberDto();
memberRequest = MemberFactory.request();
memberResponse = MemberFactory.response();
}
@Test
@DisplayName("회원 목록(페이징)")
public void memberList(){
List<Member>list = new ArrayList<>();
list.add(member);
PageRequest pageable = PageRequest.of(0,5, Sort.by("id").descending());
Page<Member>pageList = new PageImpl<>(list,pageable,0);
when(memberRepository.findAll(pageable)).thenReturn(pageList);
Page<MemberResponse>result = memberService.findAll(pageable);
assertThat(result).isNotNull();
assertThat(result.get().toList().get(0).memberName()).isEqualTo(member.getMemberName());
}
리포지터리 계층 테스트 코드
리포지터리는 디비에 저장 및 조회가 제대로 잘 작동이 되는지를 확인하는 것이 테스트의 목표이고 JPA의 도움이 필요하므로 @DataJpaTest슬라이스 테스트를 사용한다.
@DataJpaTest
@Import({TestQueryDslConfig.class})
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class MemberRepositoryTest {
@Autowired
private MemberRepository memberRepository;
@Test
@DisplayName("회원 검색 테스트")
public void memberSearchTest(){
Pageable pageable = PageRequest.of(0, 5, Sort.by("id").descending());
String keyword= "well4149";
Page<MemberResponse> result = memberRepository.findByAllSearch(keyword,pageable);
System.out.println(result);
}
}
'포폴 > Coffies Vol.02' 카테고리의 다른 글
[CoffiesVol.02] Redis의 세션과 캐시 서버를 분리하기. (0) | 2024.06.12 |
---|---|
[Coffies Vol.02] 이메일 인증 비동기 처리 (0) | 2023.12.20 |
[Coffies Vol.02]네이티브 쿼리 트러블 슈팅 (0) | 2023.07.23 |
[Coffies Vol.02]Jpa N+1 문제 (0) | 2023.05.16 |
Coffies Vol.02 프로젝트 (0) | 2023.03.26 |