SlideShare a Scribd company logo
1 of 21
Download to read offline
코드 레벨 테스트와 커버리지
Basic / Advanced / Expert
샘플 어플리케이션(스프링 기반 petclinic) 기반으로
Junit 테스트 사례 살펴보기
목차
1. 샘플 어플리케이션(애완동물 병원) 개요
2. 테스트 코드 작성 개요
. 각 레이어(Controller/Service/Repository)별 특성
. 코드 테스트에서의 단위 / 통합 테스트란
. 테스트와 테스트 커버리지와의 관계
3. 등록 기능에 대한 테스트 접근 1 – 블랙박스
4. 등록 기능에 대한 테스트 접근 2 – 화이트 박스
5. 정리
교육 개요
샘플 어플리케이션
스프링프레임워크
Controller-Service-Repository
독립된 메모리DB사용(HSQLDB)
Controller에 대한 테스트 코드
Service에 대한 테스트 코드
+) Mock을 이용한 단위 테스트
+) 테스트 코드 내 동적 쿼리 이용 예
+) 테스트 코드의 상위 클래스
이용하기
애완동물 등록하기 기능에 대해
라인/브랜치 커버리지 측정 후
테스트 코드 보완하기
애완동물 병원- spring-framework-petclinic
http://projects.spring.io/spring-petclinic/
(유스 케이스)
수의사 목록 및 그들의 전문 분야보기
애완 동물 주인에 관한 정보보기
애완 동물 주인에 관한 정보 업데이트
시스템에 새 애완 동물 주인 추가
애완 동물에 관한 정보보기
애완 동물에 관한 정보 업데이트
시스템에 새 애완 동물 추가
애완 동물의 방문 기록과 관련된 정보보기
애완 동물의 방문 기록과 관련 정보 추가
(비즈니스 규칙)
한명의 소유자는 대소 문자를 구분없이 같은 이름으로 여러 애완
동물을 가질 수 없습니다
1. 샘플 어플리케이션(애완동물병원)개요
샘플 어플리케이션 구성
출처: https://www.slideshare.net/AntoineRey/spring-framework-petclinic-sample-application
PetController
ClinicService
ClinicServiceImpl
PetRepository VetRepository
OwnerRepository VisitRepository
개발 코드
애완동물 등록 관련 클래스
1. 샘플 어플리케이션(애완동물병원)개요
애완동물 등록 - Controller/Service/Repository
출처: https://www.slideshare.net/AntoineRey/spring-framework-petclinic-sample-application
PetController
ClinicService
PetRepository
@RequestMapping(value = "/pets/new", method = RequestMethod.GET)
public String initCreationForm(Owner owner, ModelMap model) {
@RequestMapping(value = "/pets/new", method = RequestMethod.POST)
public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result, ModelMap model) {
@RequestMapping(value = "/pets/{petId}/edit", method = RequestMethod.GET)
public String initUpdateForm(@PathVariable("petId") int petId, ModelMap model) {
@RequestMapping(value = "/pets/{petId}/edit", method = RequestMethod.POST)
public String processUpdateForm(@Valid Pet pet, BindingResult result, Owner owner, ModelMap model) {
Collection<PetType> findPetTypes() throws DataAccessException;
Owner findOwnerById(int id) throws DataAccessException;
Pet findPetById(int id) throws DataAccessException;
void savePet(Pet pet) throws DataAccessException;
void saveVisit(Visit visit) throws DataAccessException;
Collection<Vet> findVets() throws DataAccessException;
void saveOwner(Owner owner) throws DataAccessException;
Collection<Owner> findOwnerByLastName(String lastName) throws DataAccessException;
Collection<Visit> findVisitsByPetId(int petId);
List<PetType> findPetTypes() throws DataAccessException;
Pet findById(int id) throws DataAccessException;
void save(Pet pet) throws DataAccessException;
1. 샘플 어플리케이션(애완동물병원)개요
※ 각 레이어(Controller/Service/Repository)별 특성
Presentation Layer Business Layer Persistence Layer
설 명
사용자의 요청을 받아들여 적
절한 서버단의 비즈니스 로직
과 연결해주고 요청처리 결과
를 받아들여 사용자의 목적에
맞게 가공하여 보여주는 일을
하는 계층
어플리케이션의 실제 비즈니
스 로직이 구현되어 있는 부분
으로, 모든 비즈니스 엔터티들
과 이를 가지고 구체적인 업무
를 수행하는 비즈니스 프로세
스들이 구현되어 있다
어플리케이션의 모든 DB 관련
작업을 담당한다. DB 작업을 비
즈니스 프로세스로부터 분리시
켜 Data Access Object(DAO)에
서 처리함으로써 차후에 데이
터베이스 서버 홖경이 변경되
더라도 유연하게 대처할 수 있
게 된다
테스트 목
표
사용자의 요청을 적절한 서버
단의 비즈니스 로직과 연결해
주고 그 결과를 적절한 사용자
화면으로 반홖하는지 확인한다
어플리케이션의 실제 비즈니
스 로직이 맞게 구현되었는지
확인한다
DB 관련 작업(조회,등록,수정,
삭제)들이 정상적으로 수행되
는지 확인한다
Junit 테스
트
우선 순위
낮음
(웹 리소스를 많이 사용하는 계
층이기 때문에 Junit 으로 별도
로 테스트하기가 어렵움, 결함
이 발생할 소지가 적으며, 결함
이 쉽게 발견되고, 수정비용이
적기 때문에 일반적으로는
junit으로 테스트를 수행하지
않음)
높음
(실제 비즈니스 로직이 맞게 구
현되었는지 반드시 검증이 되
어야 하며, 개발 완료된 DAO와
연결한 경우 DAO도 같이 테스
트가 가능하다)
중간
(DB 조회,등록,수정,삭제 등이
정상적으로 수행되는지 확인이
필요하며, 비즈니스 클래스의
로직이 복잡하거나 코드가 많
은 경우 결함의 발생 위치를 빨
리 찾기 위해 DAO에 대한 junit
테스트가 꼭 필요해 짂다)
2. 테스트코드 작성 개요
어떤 레이어, 어떤 대상에 대해 테스트 코드를 작성해야 할까?
코드 테스트에서의 단위 / 통합 테스트란
[ 단위 vs 통합 테스트 ]
단위 vs 통합으로 볼 수 있는 구분
1) 개별 메소드 vs 전체 클래스
개별 메소드별 테스트 vs 조회→등록 →수정→삭제등의 호출 흐름 테스트
2) 개별 클래스 vs 관련 클래스 묶음
클래스 하나에 대한 테스트vs 관련 클래스 여러 개에 대한 테스트
3) 개별 기능(API) vs 업무흐름 테스트
REST API 같은 기능 단위 테스트 vs 업무 흐름을 연동한 테스트
2. 테스트코드 작성 개요
이롞 적용?
단위 테스트 vs 통합 테스트? 코드 테스트는 어떤 테스트이지?
테스트와 커버리지와의 관계
2. 테스트코드 작성 개요
- 테스트 커버리지는 단지 실행이 되었는지를 표시할 뿐 테스트 결과가 유효한지를
판단해 주지는 못한다
- 다양한 커버리지 아이템 중 한 가지만 선택하기 때문에 100% 테스트 커버리지가
100% 테스트가 되었다를 의미하지는 않는다
(예를 들면 다른 입력 값을 갖는 여러 테스트 케이스가 수행되어도 커버리지는
항상 같을 수 있다)
- 코드 커버리지를 측정 하는 경우는 오직 구현된 코드에 대해서만 측정되기 때문에
구현되지 않은 요건에 대해서는 측정되지 않는다
테스트 커버리지는 단순히 테스트(실행)가 얼마나 수행되었는지 보여주는 참조지표일 뿐,
테스트 자체가 잘 수행되었는지를 보장해 주지는 못한다
ClinicService 테스트 코드 작성
3. 등록 기능에 대한 테스트 작성 – 블랙박스
[애완동물 등록에 필요한 정보]
(1)이름
(2)생년월일
(3)타입(개/고양이/…)
(4)주인정보
(firstName, lastName,
address, city, telephone)
ClinicServiceImpl#savePet(Pet pet) Pet
개발 코드 살펴보기
ClinicService 테스트 코드 작성
3. 등록 기능에 대한 테스트 작성 – 블랙박스
@Test
@Transactional
public void testSavePet_기본() throws Exception {
Owner testOwner = this.clinicService.findOwnerById(testOwnerId);
assertNotNull(testOwner);
int found = testOwner.getPets().size();
Pet newPet = new Pet();
newPet.setName("my test pet");
Collection<PetType> types = this.clinicService.findPetTypes();
newPet.setType(EntityUtils.getById(types, PetType.class, 2));
newPet.setBirthDate(TestDataUtils.string2LocalDate("2018-12-01"));
testOwner.addPet(newPet);
assertNull(newPet.getId());
assertEquals(found+1, testOwner.getPets().size());
// assertThat(testOwner.getPets().size()).isEqualTo(found + 1);
this.clinicService.savePet(newPet);
this.clinicService.saveOwner(testOwner);
testOwner = this.clinicService.findOwnerById(6);
assertEquals(found+1, testOwner.getPets().size());
// checks that id has been generated
// assertNotNull(newPet.getId());
assertThat(newPet.getId()).isNotNull();
}
public class ClinicServiceTest extends AbstractPetStoreTestCase{
private final int testOwnerId = 1;
@Autowired
ClinicService clinicService;
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
import org.junit.runner.RunWith;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/business-config.xml"})
@ActiveProfiles("jdbc")
public abstract class AbstractPetStoreTestCase {
}
테스트 코드 작성1 – SavePet 기본 테스트
AbstractPetStoreTestCase
ClinicServiceTest 선언부
ClinicServiceTest#testSavePet_기본
ClinicService 테스트 코드 작성
3. 등록 기능에 대한 테스트 작성 – 블랙박스
@Test
@Transactional
public void testSavePet_JDBCTemplate이용() throws Exception {
int testOwnerId = jdbcTemplate.queryForObject("select id from owners LIMIT 1;",
Integer.class);
Owner testOwner = this.clinicService.findOwnerById(testOwnerId);
assertNotNull(testOwner);
int found = testOwner.getPets().size();
Pet newPet = new Pet();
newPet.setName("my test pet");
Collection<PetType> types = this.clinicService.findPetTypes();
newPet.setType(EntityUtils.getById(types, PetType.class, 2));
newPet.setBirthDate(TestDataUtils.string2LocalDate("2018-12-01"));
testOwner.addPet(newPet);
assertNull(newPet.getId());
assertEquals(found+1, testOwner.getPets().size());
// assertThat(testOwner.getPets().size()).isEqualTo(found + 1);
this.clinicService.savePet(newPet);
this.clinicService.saveOwner(testOwner);
testOwner = this.clinicService.findOwnerById(testOwnerId);
assertEquals(found+1, testOwner.getPets().size());
// checks that id has been generated
assertNotNull(newPet.getId());
// assertThat(pet.getId()).isNotNull();
}
public class ClinicServiceTest extends AbstractPetStoreTestCase{
private final int testOwnerId = 1;
@Autowired
ClinicService clinicService;
@Autowired
JdbcTemplate jdbcTemplate;
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
테스트 코드 작성2 – JdbcTemplate을 이용한 동적 데이터 세팅
ClinicService 테스트 코드 작성
3. 등록 기능에 대한 테스트 작성 – 블랙박스
@Test
public void testFindPetTypes_DB에애완동물종류가없는경우() throws
Exception {
when(mockPetRepositry.findPetTypes()).thenReturn(null);
Collection<PetType> result = clinicService.findPetTypes();
assertNull(result);
verify(mockPetRepositry, atLeastOnce()).findPetTypes();
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/business-config.xml"})
@ActiveProfiles("jdbc")
public class ClinicServiceMockTest extends AbstractPetStoreTestCase{
@Mock
PetRepository mockPetRepositry;
@Autowired
VetRepository vetRepository;
@Autowired
OwnerRepository ownerRepository;
@Autowired
VisitRepository visitRepository;
ClinicService clinicService;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockPetRepositry = mock(JdbcPetRepositoryImpl.class);
clinicService = new ClinicServiceImpl(mockPetRepositry, vetRepository,
ownerRepository, visitRepository);
}
테스트 코드 작성 3-1 – Mock을 이용하여 특정 상황을 만드는 테스트
테스트 대상을 순수 클래스(POJO)화 하여 테스트
ClinicService 테스트 코드 작성
3. 등록 기능에 대한 테스트 작성 – 블랙박스
@Test
public void testFindPetTypes_DB에애완동물종류가없는경우() throws
Exception {
when(mockPetRepositry.findPetTypes()).thenReturn(null);
Collection<PetType> result = clinicService.findPetTypes();
assertNull(result);
verify(mockPetRepositry, atLeastOnce()).findPetTypes();
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/mock-config-
clinicservice.xml", "classpath:spring/business-config.xml"})
@ActiveProfiles("jdbc")
public class ClinicServiceMockConfigTest extends AbstractPetStoreTestCase{
@Autowired()
PetRepository mockPetRepositry;
@Autowired
ClinicService clinicService;
@Before
public void setUp() throws Exception {
//MockitoAnnotations.initMocks(this);
}
테스트 코드 작성 3-2 – Mock을 이용하여 특정 상황을 만드는 테스트
<bean id="mockPetRepository" class="org.mockito.Mockito" factory-method="mock"
primary="true">
<constructor-arg value="org.springframework.samples.petclinic.repository.PetRepository"/>
</bean>
테스트 리소스 폴더에 임의의 설정 파일을 추가 정의
Mocking하려는 대상에 빈 설정을 덮어씌워서 Mocking
테스트 리소스 폴더에 정의한 테스트 config을 추가
테스트 커버리지를 이용한 테스트 접근
(이클립스 플러그인 eclemma이용)
4. 등록기능에대한 테스트작성 – 화이트박스
Window>Preferences 에서 java>Code Coverage를 선택한다
개발코드에 대해서만 커버리지 측정을 하기 위해
Only path entries matchin에 “main” 을 입력한다
테스트 코드를 선택하고 Run As가 아닌 Coverage As로 테스트를 수행한다
사전 설정 테스트 실행(Coverage As)
※ EclEmma 플러그인 설치는 별도 자료 참조
테스트 커버리지를 이용한 테스트 접근
4. 등록기능에대한 테스트작성 – 화이트박스
테스트 커버리지 결과 확인(Line Counter)
1) 테스트가 수행되며, 자동으로 Coverage 뷰가 표시된다
2) 라인 커버리지 확인을 위해 Line Counter를 선택한다
3) 상세 파일을 선택하면 에디터에 해당 코드레벨에서의 커버리지
가 색으로 표시된다(초록색:cover, 빨간색:uncover, 노란색:부분적
으로cover)
테스트 커버리지를 이용한 테스트 접근
1) Branch Counter를 선택하면 분기조건에 대한 커버리
지를 확인할 수 있다
2) 코드로 보면 pet이 null인지 확인하는 분기에서 항상
false였기 때문에 분기문에 노란색, 실행 코드에 빨간색으
로 표시됨을 확인할 수 있다
Branch Counter로 브랜치 커버리지(*컨디션 커버리지) 확인
4. 등록기능에대한 테스트작성 – 화이트박스
4. 등록기능에대한 테스트작성 – 화이트박스
테스트 커버리지를 이용한 테스트 접근
분기문에 대한 테스트 추가 후 커버리지 확인
@Test(expected=PetClinicCoreException.class)
public void testSavePet_pet이Null인경우() throws Exception {
Pet newPet = null;
this.clinicService.savePet(newPet);
fail("기대한 Exception이 발생하지 않았습니다");
}
※ pet이Null인경우에 대한 테스트 추가
2) Coverage As로 테스트를 수행하고 나면 코드 상의 노란색
/빨간색 부분이 초록색으로 바뀌는 것을 확인할 수 있다
4. 등록기능에대한 테스트작성 – 화이트박스
※ Exception 발생에 대한 테스트
@Test(expected=PetClinicCoreException.class)
public void testSavePet_pet이Null인경우() throws Exception {
Pet newPet = null;
this.clinicService.savePet(newPet);
fail("기대한 Exception이 발생하지 않았습니다");
}
업무 로직상 정합하지 않는 상황에 대해 개발코드에서는
미리 정의한 임의의 Exception을 발생시킬 수 있다
“안 그래도 많이 발생하는 에러를 왜 일부러 또 발생시키나욧!!??”
※ 하위 레벨에서 사전에 정의한 기준에 따라 (임의의) Exception을
발생시키면 상위레벨에서 이를 상황별로 비즈니스 요건에 따라
다르게 처리할 수 있는 기회가 생긴다
JUnit4에서 특정 Exception이 발생해야지만
테스트가 성공하도록 하는 코드
Exception이 발생하면 fail문이 실행되지 않음
4. 등록기능에대한 테스트작성 – 화이트박스
과정 회고
좋았던 점
아쉬운 점/
개선할 점
요건(개발코드?)을 분석하고 무엇을 테스트할지 고민하기
Controller/Service/Repository 중 어떤 영역을 테스트할지 고민하기
코드 레벨에서의 단위 / 통합 테스트를 이해하고 고민해 보기
교육 목표
기본적인 테스트 코드 작성
Mock을 이용하여 특정 상황을 만들고 테스트하기
테스트 코드 안에서 동적 쿼리 실행하기
라인/브랜치 커버리지를 측정해 보고 테스트 보완하기 얘기해 보아요~
감사합니다

More Related Content

What's hot

UI 정적분석툴 소개와 활용사례
UI 정적분석툴 소개와 활용사례UI 정적분석툴 소개와 활용사례
UI 정적분석툴 소개와 활용사례SangIn Choung
 
발표자료 1인qa로살아남는6가지방법
발표자료 1인qa로살아남는6가지방법발표자료 1인qa로살아남는6가지방법
발표자료 1인qa로살아남는6가지방법SangIn Choung
 
SDET 인력 양성을 위한 프로젝트 지원 사례 정리
SDET 인력 양성을 위한 프로젝트 지원 사례 정리SDET 인력 양성을 위한 프로젝트 지원 사례 정리
SDET 인력 양성을 위한 프로젝트 지원 사례 정리SangIn Choung
 
테스트자동화 성공전략
테스트자동화 성공전략테스트자동화 성공전략
테스트자동화 성공전략SangIn Choung
 
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드 Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드 SangIn Choung
 
테스트수행사례 W통합보안솔루션
테스트수행사례 W통합보안솔루션테스트수행사례 W통합보안솔루션
테스트수행사례 W통합보안솔루션SangIn Choung
 
테스트개선지원 사례 - 웹어플리케이션대상
테스트개선지원 사례 - 웹어플리케이션대상테스트개선지원 사례 - 웹어플리케이션대상
테스트개선지원 사례 - 웹어플리케이션대상SangIn Choung
 
테스터가 말하는 테스트코드 작성 팁과 사례
테스터가 말하는 테스트코드 작성 팁과 사례테스터가 말하는 테스트코드 작성 팁과 사례
테스터가 말하는 테스트코드 작성 팁과 사례SangIn Choung
 
UI빈발결함 및 테스트의 필요성 초기교육자료
UI빈발결함 및 테스트의 필요성 초기교육자료UI빈발결함 및 테스트의 필요성 초기교육자료
UI빈발결함 및 테스트의 필요성 초기교육자료SangIn Choung
 
Io t에서의 소프트웨어단위테스트_접근사례
Io t에서의 소프트웨어단위테스트_접근사례Io t에서의 소프트웨어단위테스트_접근사례
Io t에서의 소프트웨어단위테스트_접근사례SangIn Choung
 
애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)
애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)
애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)SangIn Choung
 
테스트자동화와 TDD
테스트자동화와 TDD테스트자동화와 TDD
테스트자동화와 TDDSunghyouk Bae
 
애자일 테스트 프랙티스와 사례들 (부제: 협업의 힘)
애자일 테스트 프랙티스와 사례들 (부제: 협업의 힘)애자일 테스트 프랙티스와 사례들 (부제: 협업의 힘)
애자일 테스트 프랙티스와 사례들 (부제: 협업의 힘)SangIn Choung
 
(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)
(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)
(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)SangIn Choung
 
testing for agile?, agile for testing
testing for agile?, agile for testingtesting for agile?, agile for testing
testing for agile?, agile for testingSangIn Choung
 
크로스(멀티)브라우저 테스트수행가이드
크로스(멀티)브라우저 테스트수행가이드크로스(멀티)브라우저 테스트수행가이드
크로스(멀티)브라우저 테스트수행가이드SangIn Choung
 
위험기반테스트접근 테스트계획 사례
위험기반테스트접근 테스트계획 사례위험기반테스트접근 테스트계획 사례
위험기반테스트접근 테스트계획 사례SangIn Choung
 
Rest api 테스트 수행가이드
Rest api 테스트 수행가이드Rest api 테스트 수행가이드
Rest api 테스트 수행가이드SangIn Choung
 
HPE 솔루션과 함께하는 모바일 앱 테스팅 방안 소개
HPE 솔루션과 함께하는 모바일 앱 테스팅 방안 소개HPE 솔루션과 함께하는 모바일 앱 테스팅 방안 소개
HPE 솔루션과 함께하는 모바일 앱 테스팅 방안 소개Ki Bae Kim
 
jacoco를 이용한 매뉴얼 테스트의 서버사이드 코드 커버리지 측정하기
jacoco를 이용한 매뉴얼 테스트의 서버사이드 코드 커버리지 측정하기jacoco를 이용한 매뉴얼 테스트의 서버사이드 코드 커버리지 측정하기
jacoco를 이용한 매뉴얼 테스트의 서버사이드 코드 커버리지 측정하기SangIn Choung
 

What's hot (20)

UI 정적분석툴 소개와 활용사례
UI 정적분석툴 소개와 활용사례UI 정적분석툴 소개와 활용사례
UI 정적분석툴 소개와 활용사례
 
발표자료 1인qa로살아남는6가지방법
발표자료 1인qa로살아남는6가지방법발표자료 1인qa로살아남는6가지방법
발표자료 1인qa로살아남는6가지방법
 
SDET 인력 양성을 위한 프로젝트 지원 사례 정리
SDET 인력 양성을 위한 프로젝트 지원 사례 정리SDET 인력 양성을 위한 프로젝트 지원 사례 정리
SDET 인력 양성을 위한 프로젝트 지원 사례 정리
 
테스트자동화 성공전략
테스트자동화 성공전략테스트자동화 성공전략
테스트자동화 성공전략
 
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드 Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
 
테스트수행사례 W통합보안솔루션
테스트수행사례 W통합보안솔루션테스트수행사례 W통합보안솔루션
테스트수행사례 W통합보안솔루션
 
테스트개선지원 사례 - 웹어플리케이션대상
테스트개선지원 사례 - 웹어플리케이션대상테스트개선지원 사례 - 웹어플리케이션대상
테스트개선지원 사례 - 웹어플리케이션대상
 
테스터가 말하는 테스트코드 작성 팁과 사례
테스터가 말하는 테스트코드 작성 팁과 사례테스터가 말하는 테스트코드 작성 팁과 사례
테스터가 말하는 테스트코드 작성 팁과 사례
 
UI빈발결함 및 테스트의 필요성 초기교육자료
UI빈발결함 및 테스트의 필요성 초기교육자료UI빈발결함 및 테스트의 필요성 초기교육자료
UI빈발결함 및 테스트의 필요성 초기교육자료
 
Io t에서의 소프트웨어단위테스트_접근사례
Io t에서의 소프트웨어단위테스트_접근사례Io t에서의 소프트웨어단위테스트_접근사례
Io t에서의 소프트웨어단위테스트_접근사례
 
애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)
애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)
애자일과 애자일 테스트 소개 (테스트기본교육 3장 2절)
 
테스트자동화와 TDD
테스트자동화와 TDD테스트자동화와 TDD
테스트자동화와 TDD
 
애자일 테스트 프랙티스와 사례들 (부제: 협업의 힘)
애자일 테스트 프랙티스와 사례들 (부제: 협업의 힘)애자일 테스트 프랙티스와 사례들 (부제: 협업의 힘)
애자일 테스트 프랙티스와 사례들 (부제: 협업의 힘)
 
(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)
(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)
(편집-테스트카페 발표자료) 1인 QA 수행사례로 발표한 자료 (W프로젝트 사례)
 
testing for agile?, agile for testing
testing for agile?, agile for testingtesting for agile?, agile for testing
testing for agile?, agile for testing
 
크로스(멀티)브라우저 테스트수행가이드
크로스(멀티)브라우저 테스트수행가이드크로스(멀티)브라우저 테스트수행가이드
크로스(멀티)브라우저 테스트수행가이드
 
위험기반테스트접근 테스트계획 사례
위험기반테스트접근 테스트계획 사례위험기반테스트접근 테스트계획 사례
위험기반테스트접근 테스트계획 사례
 
Rest api 테스트 수행가이드
Rest api 테스트 수행가이드Rest api 테스트 수행가이드
Rest api 테스트 수행가이드
 
HPE 솔루션과 함께하는 모바일 앱 테스팅 방안 소개
HPE 솔루션과 함께하는 모바일 앱 테스팅 방안 소개HPE 솔루션과 함께하는 모바일 앱 테스팅 방안 소개
HPE 솔루션과 함께하는 모바일 앱 테스팅 방안 소개
 
jacoco를 이용한 매뉴얼 테스트의 서버사이드 코드 커버리지 측정하기
jacoco를 이용한 매뉴얼 테스트의 서버사이드 코드 커버리지 측정하기jacoco를 이용한 매뉴얼 테스트의 서버사이드 코드 커버리지 측정하기
jacoco를 이용한 매뉴얼 테스트의 서버사이드 코드 커버리지 측정하기
 

Similar to [고급과정] 코드 테스트와 커버리지 교육(실습위주)

10장 결과 검증
10장 결과 검증10장 결과 검증
10장 결과 검증dagri82
 
시작하자 단위테스트
시작하자 단위테스트시작하자 단위테스트
시작하자 단위테스트YongEun Choi
 
回国去哪买毕业证办迪肯大学毕业证Deakin毕业证书【Q微202-661-4433】 Deakin售澳洲毕业证原版新毕业证书出售各国毕业证买澳洲毕业证的价...
回国去哪买毕业证办迪肯大学毕业证Deakin毕业证书【Q微202-661-4433】 Deakin售澳洲毕业证原版新毕业证书出售各国毕业证买澳洲毕业证的价...回国去哪买毕业证办迪肯大学毕业证Deakin毕业证书【Q微202-661-4433】 Deakin售澳洲毕业证原版新毕业证书出售各国毕业证买澳洲毕业证的价...
回国去哪买毕业证办迪肯大学毕业证Deakin毕业证书【Q微202-661-4433】 Deakin售澳洲毕业证原版新毕业证书出售各国毕业证买澳洲毕业证的价...asfasf4
 
Sonarqube 20160509
Sonarqube 20160509Sonarqube 20160509
Sonarqube 20160509영석 조
 
Online service 계층별 성능 모니터링 방안
Online service 계층별 성능 모니터링 방안Online service 계층별 성능 모니터링 방안
Online service 계층별 성능 모니터링 방안중선 곽
 
단위테스트자동화지원도구 임성현 최종
단위테스트자동화지원도구 임성현 최종단위테스트자동화지원도구 임성현 최종
단위테스트자동화지원도구 임성현 최종guest7178884
 
Tr#3 5) 임성현 책임
Tr#3 5) 임성현 책임Tr#3 5) 임성현 책임
Tr#3 5) 임성현 책임Lim SungHyun
 
Droid knights android test @Droid Knights 2018
Droid knights android test @Droid Knights 2018Droid knights android test @Droid Knights 2018
Droid knights android test @Droid Knights 2018KyungHo Jung
 
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)Suwon Chae
 
Spring 3의 jsr 303 지원
Spring 3의 jsr 303 지원Spring 3의 jsr 303 지원
Spring 3의 jsr 303 지원Sewon Ann
 
[2018] MyBatis에서 JPA로
[2018] MyBatis에서 JPA로[2018] MyBatis에서 JPA로
[2018] MyBatis에서 JPA로NHN FORWARD
 
유지보수 가능한 개발 원칙
유지보수 가능한 개발 원칙유지보수 가능한 개발 원칙
유지보수 가능한 개발 원칙Hyosang Hong
 
Java 유지보수 가능한 개발 원칙
Java 유지보수 가능한 개발 원칙Java 유지보수 가능한 개발 원칙
Java 유지보수 가능한 개발 원칙Hyosang Hong
 
자바 테스트 자동화
자바 테스트 자동화자바 테스트 자동화
자바 테스트 자동화Sungchul Park
 
Spring test mvc 발표자료
Spring test mvc 발표자료Spring test mvc 발표자료
Spring test mvc 발표자료수홍 이
 
소프트웨어 개선 그룹(Sig) 개발 원칙
소프트웨어 개선 그룹(Sig) 개발 원칙소프트웨어 개선 그룹(Sig) 개발 원칙
소프트웨어 개선 그룹(Sig) 개발 원칙Hong Hyo Sang
 
TDD.JUnit.조금더.알기
TDD.JUnit.조금더.알기TDD.JUnit.조금더.알기
TDD.JUnit.조금더.알기Wonchang Song
 
투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2
투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2
투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2tobeware
 

Similar to [고급과정] 코드 테스트와 커버리지 교육(실습위주) (20)

10장 결과 검증
10장 결과 검증10장 결과 검증
10장 결과 검증
 
시작하자 단위테스트
시작하자 단위테스트시작하자 단위테스트
시작하자 단위테스트
 
回国去哪买毕业证办迪肯大学毕业证Deakin毕业证书【Q微202-661-4433】 Deakin售澳洲毕业证原版新毕业证书出售各国毕业证买澳洲毕业证的价...
回国去哪买毕业证办迪肯大学毕业证Deakin毕业证书【Q微202-661-4433】 Deakin售澳洲毕业证原版新毕业证书出售各国毕业证买澳洲毕业证的价...回国去哪买毕业证办迪肯大学毕业证Deakin毕业证书【Q微202-661-4433】 Deakin售澳洲毕业证原版新毕业证书出售各国毕业证买澳洲毕业证的价...
回国去哪买毕业证办迪肯大学毕业证Deakin毕业证书【Q微202-661-4433】 Deakin售澳洲毕业证原版新毕业证书出售各国毕业证买澳洲毕业证的价...
 
Cygnus unit test
Cygnus unit testCygnus unit test
Cygnus unit test
 
Sonarqube 20160509
Sonarqube 20160509Sonarqube 20160509
Sonarqube 20160509
 
Android unit testing
Android unit testingAndroid unit testing
Android unit testing
 
Online service 계층별 성능 모니터링 방안
Online service 계층별 성능 모니터링 방안Online service 계층별 성능 모니터링 방안
Online service 계층별 성능 모니터링 방안
 
단위테스트자동화지원도구 임성현 최종
단위테스트자동화지원도구 임성현 최종단위테스트자동화지원도구 임성현 최종
단위테스트자동화지원도구 임성현 최종
 
Tr#3 5) 임성현 책임
Tr#3 5) 임성현 책임Tr#3 5) 임성현 책임
Tr#3 5) 임성현 책임
 
Droid knights android test @Droid Knights 2018
Droid knights android test @Droid Knights 2018Droid knights android test @Droid Knights 2018
Droid knights android test @Droid Knights 2018
 
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
 
Spring 3의 jsr 303 지원
Spring 3의 jsr 303 지원Spring 3의 jsr 303 지원
Spring 3의 jsr 303 지원
 
[2018] MyBatis에서 JPA로
[2018] MyBatis에서 JPA로[2018] MyBatis에서 JPA로
[2018] MyBatis에서 JPA로
 
유지보수 가능한 개발 원칙
유지보수 가능한 개발 원칙유지보수 가능한 개발 원칙
유지보수 가능한 개발 원칙
 
Java 유지보수 가능한 개발 원칙
Java 유지보수 가능한 개발 원칙Java 유지보수 가능한 개발 원칙
Java 유지보수 가능한 개발 원칙
 
자바 테스트 자동화
자바 테스트 자동화자바 테스트 자동화
자바 테스트 자동화
 
Spring test mvc 발표자료
Spring test mvc 발표자료Spring test mvc 발표자료
Spring test mvc 발표자료
 
소프트웨어 개선 그룹(Sig) 개발 원칙
소프트웨어 개선 그룹(Sig) 개발 원칙소프트웨어 개선 그룹(Sig) 개발 원칙
소프트웨어 개선 그룹(Sig) 개발 원칙
 
TDD.JUnit.조금더.알기
TDD.JUnit.조금더.알기TDD.JUnit.조금더.알기
TDD.JUnit.조금더.알기
 
투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2
투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2
투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2
 

More from SangIn Choung

기본적인 테스트에 대한 pytest 자동화 접근
기본적인 테스트에 대한 pytest 자동화 접근기본적인 테스트에 대한 pytest 자동화 접근
기본적인 테스트에 대한 pytest 자동화 접근SangIn Choung
 
짝 테스트(Pair Testing) 소개와 사례
짝 테스트(Pair Testing) 소개와 사례짝 테스트(Pair Testing) 소개와 사례
짝 테스트(Pair Testing) 소개와 사례SangIn Choung
 
코드 테스트와 커버리지 관련 설문 및 개선계획수립 in 2018
코드 테스트와 커버리지 관련 설문 및 개선계획수립 in 2018코드 테스트와 커버리지 관련 설문 및 개선계획수립 in 2018
코드 테스트와 커버리지 관련 설문 및 개선계획수립 in 2018SangIn Choung
 
엔지니어링관점에서 테스트 개선방안 질의 응답
엔지니어링관점에서 테스트 개선방안 질의 응답엔지니어링관점에서 테스트 개선방안 질의 응답
엔지니어링관점에서 테스트 개선방안 질의 응답SangIn Choung
 
When develpment met test(shift left testing)
When develpment met test(shift left testing)When develpment met test(shift left testing)
When develpment met test(shift left testing)SangIn Choung
 
테스터도 알아야 할 웹 개발(테스트 교육 3장 1절 부분발췌)
테스터도 알아야 할 웹 개발(테스트 교육 3장 1절 부분발췌)테스터도 알아야 할 웹 개발(테스트 교육 3장 1절 부분발췌)
테스터도 알아야 할 웹 개발(테스트 교육 3장 1절 부분발췌)SangIn Choung
 

More from SangIn Choung (8)

기본적인 테스트에 대한 pytest 자동화 접근
기본적인 테스트에 대한 pytest 자동화 접근기본적인 테스트에 대한 pytest 자동화 접근
기본적인 테스트에 대한 pytest 자동화 접근
 
짝 테스트(Pair Testing) 소개와 사례
짝 테스트(Pair Testing) 소개와 사례짝 테스트(Pair Testing) 소개와 사례
짝 테스트(Pair Testing) 소개와 사례
 
코드 테스트와 커버리지 관련 설문 및 개선계획수립 in 2018
코드 테스트와 커버리지 관련 설문 및 개선계획수립 in 2018코드 테스트와 커버리지 관련 설문 및 개선계획수립 in 2018
코드 테스트와 커버리지 관련 설문 및 개선계획수립 in 2018
 
sdet수행 사례
sdet수행 사례sdet수행 사례
sdet수행 사례
 
엔지니어링관점에서 테스트 개선방안 질의 응답
엔지니어링관점에서 테스트 개선방안 질의 응답엔지니어링관점에서 테스트 개선방안 질의 응답
엔지니어링관점에서 테스트 개선방안 질의 응답
 
Coded ui가이드
Coded ui가이드Coded ui가이드
Coded ui가이드
 
When develpment met test(shift left testing)
When develpment met test(shift left testing)When develpment met test(shift left testing)
When develpment met test(shift left testing)
 
테스터도 알아야 할 웹 개발(테스트 교육 3장 1절 부분발췌)
테스터도 알아야 할 웹 개발(테스트 교육 3장 1절 부분발췌)테스터도 알아야 할 웹 개발(테스트 교육 3장 1절 부분발췌)
테스터도 알아야 할 웹 개발(테스트 교육 3장 1절 부분발췌)
 

[고급과정] 코드 테스트와 커버리지 교육(실습위주)

  • 1. 코드 레벨 테스트와 커버리지 Basic / Advanced / Expert 샘플 어플리케이션(스프링 기반 petclinic) 기반으로 Junit 테스트 사례 살펴보기
  • 2. 목차 1. 샘플 어플리케이션(애완동물 병원) 개요 2. 테스트 코드 작성 개요 . 각 레이어(Controller/Service/Repository)별 특성 . 코드 테스트에서의 단위 / 통합 테스트란 . 테스트와 테스트 커버리지와의 관계 3. 등록 기능에 대한 테스트 접근 1 – 블랙박스 4. 등록 기능에 대한 테스트 접근 2 – 화이트 박스 5. 정리
  • 3. 교육 개요 샘플 어플리케이션 스프링프레임워크 Controller-Service-Repository 독립된 메모리DB사용(HSQLDB) Controller에 대한 테스트 코드 Service에 대한 테스트 코드 +) Mock을 이용한 단위 테스트 +) 테스트 코드 내 동적 쿼리 이용 예 +) 테스트 코드의 상위 클래스 이용하기 애완동물 등록하기 기능에 대해 라인/브랜치 커버리지 측정 후 테스트 코드 보완하기
  • 4. 애완동물 병원- spring-framework-petclinic http://projects.spring.io/spring-petclinic/ (유스 케이스) 수의사 목록 및 그들의 전문 분야보기 애완 동물 주인에 관한 정보보기 애완 동물 주인에 관한 정보 업데이트 시스템에 새 애완 동물 주인 추가 애완 동물에 관한 정보보기 애완 동물에 관한 정보 업데이트 시스템에 새 애완 동물 추가 애완 동물의 방문 기록과 관련된 정보보기 애완 동물의 방문 기록과 관련 정보 추가 (비즈니스 규칙) 한명의 소유자는 대소 문자를 구분없이 같은 이름으로 여러 애완 동물을 가질 수 없습니다 1. 샘플 어플리케이션(애완동물병원)개요
  • 5. 샘플 어플리케이션 구성 출처: https://www.slideshare.net/AntoineRey/spring-framework-petclinic-sample-application PetController ClinicService ClinicServiceImpl PetRepository VetRepository OwnerRepository VisitRepository 개발 코드 애완동물 등록 관련 클래스 1. 샘플 어플리케이션(애완동물병원)개요
  • 6. 애완동물 등록 - Controller/Service/Repository 출처: https://www.slideshare.net/AntoineRey/spring-framework-petclinic-sample-application PetController ClinicService PetRepository @RequestMapping(value = "/pets/new", method = RequestMethod.GET) public String initCreationForm(Owner owner, ModelMap model) { @RequestMapping(value = "/pets/new", method = RequestMethod.POST) public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult result, ModelMap model) { @RequestMapping(value = "/pets/{petId}/edit", method = RequestMethod.GET) public String initUpdateForm(@PathVariable("petId") int petId, ModelMap model) { @RequestMapping(value = "/pets/{petId}/edit", method = RequestMethod.POST) public String processUpdateForm(@Valid Pet pet, BindingResult result, Owner owner, ModelMap model) { Collection<PetType> findPetTypes() throws DataAccessException; Owner findOwnerById(int id) throws DataAccessException; Pet findPetById(int id) throws DataAccessException; void savePet(Pet pet) throws DataAccessException; void saveVisit(Visit visit) throws DataAccessException; Collection<Vet> findVets() throws DataAccessException; void saveOwner(Owner owner) throws DataAccessException; Collection<Owner> findOwnerByLastName(String lastName) throws DataAccessException; Collection<Visit> findVisitsByPetId(int petId); List<PetType> findPetTypes() throws DataAccessException; Pet findById(int id) throws DataAccessException; void save(Pet pet) throws DataAccessException; 1. 샘플 어플리케이션(애완동물병원)개요
  • 7. ※ 각 레이어(Controller/Service/Repository)별 특성 Presentation Layer Business Layer Persistence Layer 설 명 사용자의 요청을 받아들여 적 절한 서버단의 비즈니스 로직 과 연결해주고 요청처리 결과 를 받아들여 사용자의 목적에 맞게 가공하여 보여주는 일을 하는 계층 어플리케이션의 실제 비즈니 스 로직이 구현되어 있는 부분 으로, 모든 비즈니스 엔터티들 과 이를 가지고 구체적인 업무 를 수행하는 비즈니스 프로세 스들이 구현되어 있다 어플리케이션의 모든 DB 관련 작업을 담당한다. DB 작업을 비 즈니스 프로세스로부터 분리시 켜 Data Access Object(DAO)에 서 처리함으로써 차후에 데이 터베이스 서버 홖경이 변경되 더라도 유연하게 대처할 수 있 게 된다 테스트 목 표 사용자의 요청을 적절한 서버 단의 비즈니스 로직과 연결해 주고 그 결과를 적절한 사용자 화면으로 반홖하는지 확인한다 어플리케이션의 실제 비즈니 스 로직이 맞게 구현되었는지 확인한다 DB 관련 작업(조회,등록,수정, 삭제)들이 정상적으로 수행되 는지 확인한다 Junit 테스 트 우선 순위 낮음 (웹 리소스를 많이 사용하는 계 층이기 때문에 Junit 으로 별도 로 테스트하기가 어렵움, 결함 이 발생할 소지가 적으며, 결함 이 쉽게 발견되고, 수정비용이 적기 때문에 일반적으로는 junit으로 테스트를 수행하지 않음) 높음 (실제 비즈니스 로직이 맞게 구 현되었는지 반드시 검증이 되 어야 하며, 개발 완료된 DAO와 연결한 경우 DAO도 같이 테스 트가 가능하다) 중간 (DB 조회,등록,수정,삭제 등이 정상적으로 수행되는지 확인이 필요하며, 비즈니스 클래스의 로직이 복잡하거나 코드가 많 은 경우 결함의 발생 위치를 빨 리 찾기 위해 DAO에 대한 junit 테스트가 꼭 필요해 짂다) 2. 테스트코드 작성 개요 어떤 레이어, 어떤 대상에 대해 테스트 코드를 작성해야 할까?
  • 8. 코드 테스트에서의 단위 / 통합 테스트란 [ 단위 vs 통합 테스트 ] 단위 vs 통합으로 볼 수 있는 구분 1) 개별 메소드 vs 전체 클래스 개별 메소드별 테스트 vs 조회→등록 →수정→삭제등의 호출 흐름 테스트 2) 개별 클래스 vs 관련 클래스 묶음 클래스 하나에 대한 테스트vs 관련 클래스 여러 개에 대한 테스트 3) 개별 기능(API) vs 업무흐름 테스트 REST API 같은 기능 단위 테스트 vs 업무 흐름을 연동한 테스트 2. 테스트코드 작성 개요 이롞 적용? 단위 테스트 vs 통합 테스트? 코드 테스트는 어떤 테스트이지?
  • 9. 테스트와 커버리지와의 관계 2. 테스트코드 작성 개요 - 테스트 커버리지는 단지 실행이 되었는지를 표시할 뿐 테스트 결과가 유효한지를 판단해 주지는 못한다 - 다양한 커버리지 아이템 중 한 가지만 선택하기 때문에 100% 테스트 커버리지가 100% 테스트가 되었다를 의미하지는 않는다 (예를 들면 다른 입력 값을 갖는 여러 테스트 케이스가 수행되어도 커버리지는 항상 같을 수 있다) - 코드 커버리지를 측정 하는 경우는 오직 구현된 코드에 대해서만 측정되기 때문에 구현되지 않은 요건에 대해서는 측정되지 않는다 테스트 커버리지는 단순히 테스트(실행)가 얼마나 수행되었는지 보여주는 참조지표일 뿐, 테스트 자체가 잘 수행되었는지를 보장해 주지는 못한다
  • 10. ClinicService 테스트 코드 작성 3. 등록 기능에 대한 테스트 작성 – 블랙박스 [애완동물 등록에 필요한 정보] (1)이름 (2)생년월일 (3)타입(개/고양이/…) (4)주인정보 (firstName, lastName, address, city, telephone) ClinicServiceImpl#savePet(Pet pet) Pet 개발 코드 살펴보기
  • 11. ClinicService 테스트 코드 작성 3. 등록 기능에 대한 테스트 작성 – 블랙박스 @Test @Transactional public void testSavePet_기본() throws Exception { Owner testOwner = this.clinicService.findOwnerById(testOwnerId); assertNotNull(testOwner); int found = testOwner.getPets().size(); Pet newPet = new Pet(); newPet.setName("my test pet"); Collection<PetType> types = this.clinicService.findPetTypes(); newPet.setType(EntityUtils.getById(types, PetType.class, 2)); newPet.setBirthDate(TestDataUtils.string2LocalDate("2018-12-01")); testOwner.addPet(newPet); assertNull(newPet.getId()); assertEquals(found+1, testOwner.getPets().size()); // assertThat(testOwner.getPets().size()).isEqualTo(found + 1); this.clinicService.savePet(newPet); this.clinicService.saveOwner(testOwner); testOwner = this.clinicService.findOwnerById(6); assertEquals(found+1, testOwner.getPets().size()); // checks that id has been generated // assertNotNull(newPet.getId()); assertThat(newPet.getId()).isNotNull(); } public class ClinicServiceTest extends AbstractPetStoreTestCase{ private final int testOwnerId = 1; @Autowired ClinicService clinicService; @Before public void setUp() throws Exception { } @After public void tearDown() throws Exception { } import org.junit.runner.RunWith; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:spring/business-config.xml"}) @ActiveProfiles("jdbc") public abstract class AbstractPetStoreTestCase { } 테스트 코드 작성1 – SavePet 기본 테스트 AbstractPetStoreTestCase ClinicServiceTest 선언부 ClinicServiceTest#testSavePet_기본
  • 12. ClinicService 테스트 코드 작성 3. 등록 기능에 대한 테스트 작성 – 블랙박스 @Test @Transactional public void testSavePet_JDBCTemplate이용() throws Exception { int testOwnerId = jdbcTemplate.queryForObject("select id from owners LIMIT 1;", Integer.class); Owner testOwner = this.clinicService.findOwnerById(testOwnerId); assertNotNull(testOwner); int found = testOwner.getPets().size(); Pet newPet = new Pet(); newPet.setName("my test pet"); Collection<PetType> types = this.clinicService.findPetTypes(); newPet.setType(EntityUtils.getById(types, PetType.class, 2)); newPet.setBirthDate(TestDataUtils.string2LocalDate("2018-12-01")); testOwner.addPet(newPet); assertNull(newPet.getId()); assertEquals(found+1, testOwner.getPets().size()); // assertThat(testOwner.getPets().size()).isEqualTo(found + 1); this.clinicService.savePet(newPet); this.clinicService.saveOwner(testOwner); testOwner = this.clinicService.findOwnerById(testOwnerId); assertEquals(found+1, testOwner.getPets().size()); // checks that id has been generated assertNotNull(newPet.getId()); // assertThat(pet.getId()).isNotNull(); } public class ClinicServiceTest extends AbstractPetStoreTestCase{ private final int testOwnerId = 1; @Autowired ClinicService clinicService; @Autowired JdbcTemplate jdbcTemplate; @Before public void setUp() throws Exception { } @After public void tearDown() throws Exception { } 테스트 코드 작성2 – JdbcTemplate을 이용한 동적 데이터 세팅
  • 13. ClinicService 테스트 코드 작성 3. 등록 기능에 대한 테스트 작성 – 블랙박스 @Test public void testFindPetTypes_DB에애완동물종류가없는경우() throws Exception { when(mockPetRepositry.findPetTypes()).thenReturn(null); Collection<PetType> result = clinicService.findPetTypes(); assertNull(result); verify(mockPetRepositry, atLeastOnce()).findPetTypes(); } @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:spring/business-config.xml"}) @ActiveProfiles("jdbc") public class ClinicServiceMockTest extends AbstractPetStoreTestCase{ @Mock PetRepository mockPetRepositry; @Autowired VetRepository vetRepository; @Autowired OwnerRepository ownerRepository; @Autowired VisitRepository visitRepository; ClinicService clinicService; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mockPetRepositry = mock(JdbcPetRepositoryImpl.class); clinicService = new ClinicServiceImpl(mockPetRepositry, vetRepository, ownerRepository, visitRepository); } 테스트 코드 작성 3-1 – Mock을 이용하여 특정 상황을 만드는 테스트 테스트 대상을 순수 클래스(POJO)화 하여 테스트
  • 14. ClinicService 테스트 코드 작성 3. 등록 기능에 대한 테스트 작성 – 블랙박스 @Test public void testFindPetTypes_DB에애완동물종류가없는경우() throws Exception { when(mockPetRepositry.findPetTypes()).thenReturn(null); Collection<PetType> result = clinicService.findPetTypes(); assertNull(result); verify(mockPetRepositry, atLeastOnce()).findPetTypes(); } @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:spring/mock-config- clinicservice.xml", "classpath:spring/business-config.xml"}) @ActiveProfiles("jdbc") public class ClinicServiceMockConfigTest extends AbstractPetStoreTestCase{ @Autowired() PetRepository mockPetRepositry; @Autowired ClinicService clinicService; @Before public void setUp() throws Exception { //MockitoAnnotations.initMocks(this); } 테스트 코드 작성 3-2 – Mock을 이용하여 특정 상황을 만드는 테스트 <bean id="mockPetRepository" class="org.mockito.Mockito" factory-method="mock" primary="true"> <constructor-arg value="org.springframework.samples.petclinic.repository.PetRepository"/> </bean> 테스트 리소스 폴더에 임의의 설정 파일을 추가 정의 Mocking하려는 대상에 빈 설정을 덮어씌워서 Mocking 테스트 리소스 폴더에 정의한 테스트 config을 추가
  • 15. 테스트 커버리지를 이용한 테스트 접근 (이클립스 플러그인 eclemma이용) 4. 등록기능에대한 테스트작성 – 화이트박스 Window>Preferences 에서 java>Code Coverage를 선택한다 개발코드에 대해서만 커버리지 측정을 하기 위해 Only path entries matchin에 “main” 을 입력한다 테스트 코드를 선택하고 Run As가 아닌 Coverage As로 테스트를 수행한다 사전 설정 테스트 실행(Coverage As) ※ EclEmma 플러그인 설치는 별도 자료 참조
  • 16. 테스트 커버리지를 이용한 테스트 접근 4. 등록기능에대한 테스트작성 – 화이트박스 테스트 커버리지 결과 확인(Line Counter) 1) 테스트가 수행되며, 자동으로 Coverage 뷰가 표시된다 2) 라인 커버리지 확인을 위해 Line Counter를 선택한다 3) 상세 파일을 선택하면 에디터에 해당 코드레벨에서의 커버리지 가 색으로 표시된다(초록색:cover, 빨간색:uncover, 노란색:부분적 으로cover)
  • 17. 테스트 커버리지를 이용한 테스트 접근 1) Branch Counter를 선택하면 분기조건에 대한 커버리 지를 확인할 수 있다 2) 코드로 보면 pet이 null인지 확인하는 분기에서 항상 false였기 때문에 분기문에 노란색, 실행 코드에 빨간색으 로 표시됨을 확인할 수 있다 Branch Counter로 브랜치 커버리지(*컨디션 커버리지) 확인 4. 등록기능에대한 테스트작성 – 화이트박스
  • 18. 4. 등록기능에대한 테스트작성 – 화이트박스 테스트 커버리지를 이용한 테스트 접근 분기문에 대한 테스트 추가 후 커버리지 확인 @Test(expected=PetClinicCoreException.class) public void testSavePet_pet이Null인경우() throws Exception { Pet newPet = null; this.clinicService.savePet(newPet); fail("기대한 Exception이 발생하지 않았습니다"); } ※ pet이Null인경우에 대한 테스트 추가 2) Coverage As로 테스트를 수행하고 나면 코드 상의 노란색 /빨간색 부분이 초록색으로 바뀌는 것을 확인할 수 있다
  • 19. 4. 등록기능에대한 테스트작성 – 화이트박스 ※ Exception 발생에 대한 테스트 @Test(expected=PetClinicCoreException.class) public void testSavePet_pet이Null인경우() throws Exception { Pet newPet = null; this.clinicService.savePet(newPet); fail("기대한 Exception이 발생하지 않았습니다"); } 업무 로직상 정합하지 않는 상황에 대해 개발코드에서는 미리 정의한 임의의 Exception을 발생시킬 수 있다 “안 그래도 많이 발생하는 에러를 왜 일부러 또 발생시키나욧!!??” ※ 하위 레벨에서 사전에 정의한 기준에 따라 (임의의) Exception을 발생시키면 상위레벨에서 이를 상황별로 비즈니스 요건에 따라 다르게 처리할 수 있는 기회가 생긴다 JUnit4에서 특정 Exception이 발생해야지만 테스트가 성공하도록 하는 코드 Exception이 발생하면 fail문이 실행되지 않음
  • 20. 4. 등록기능에대한 테스트작성 – 화이트박스 과정 회고 좋았던 점 아쉬운 점/ 개선할 점 요건(개발코드?)을 분석하고 무엇을 테스트할지 고민하기 Controller/Service/Repository 중 어떤 영역을 테스트할지 고민하기 코드 레벨에서의 단위 / 통합 테스트를 이해하고 고민해 보기 교육 목표 기본적인 테스트 코드 작성 Mock을 이용하여 특정 상황을 만들고 테스트하기 테스트 코드 안에서 동적 쿼리 실행하기 라인/브랜치 커버리지를 측정해 보고 테스트 보완하기 얘기해 보아요~