목차

    JUnit 순서

    테스트가 수행될 때는 @Test 만 수행되는 것이 아닌 @BeforeClass, 생성자, @Before, @AfterClass 가 존재합니다.

    생명주기를 이해하기 쉬운 방법 중 하나는 카운터를 세는 코드를 작성하시면 조금도 쉽게 이해할 수 있습니다.

    일단 코드를 먼저 작성하겠습니다. (테스트 코드 기준은 JUnit4 입니다.)

    public class JUnitLifecycle {
        private static int counter = 0;
    
        @BeforeClass
        public static void suiteSetup() {
            assertEquals(0, counter);
            counter++;
        }
    
        public JUnitLifecycle() {
            assertTrue(Arrays.asList(1, 5).contains(counter));
            counter++;
        }
    
        @Before
        public void prepareTest() {
            assertTrue(Arrays.asList(2, 6).contains(counter));
            counter++;
        }
    
        @Test
        public void peformFirstTest() {
            assertTrue(Arrays.asList(3, 7).contains(counter));
            counter++;
        }
    
        @Test
        public void performSecondTest() {
            assertTrue(Arrays.asList(3, 7).contains(counter));
            counter++;
        }
    
        @After
        public void cleanupTest() {
            assertTrue(Arrays.asList(4, 8).contains(counter));
            counter++;
        }
    
        @AfterClass
        public static void suiteFinished() {
            assertEquals(9, counter);
        }
    }

    1. @BeforeClass

    테스트 클래스 시작 시 한번만 수행

    -------------------------------------------------

    2. 생성자

    - 테스트 케이스 시작 전 호출

    - (@Before/@After 이외에도 생성자도 호출이 되는 걸 인지해주세요)

     

    3. @Before

    - 테스트 케이스 시작 전 호출

    - 테스트 값/상태 준비

     

    4. @Test

    - 테스트 케이스

    - public이고 void 타입 값으로 반환

     

    5. @After

    테스트 케이스 종료 후 호출

    - 테스트 후 정리

    -------------------------------------------------

    6. @AfterClass

    모든 테스트 케이스 완료 후, 테스트 클래스 종료 전 수행

     

    마지막 AfterClass 종료 시 카운터는 9인걸 보실 수 있습니다. 이유는 @Test(테스트 케이스)가 2번 호출이 되어 생성자와 @Before/@After 가 다시 한번 호출이 되었기 때문입니다.

    핵심 메소드

    assertEquals 			// 두개의 객체가 같은지 확인
    assertTrue / asertFalse	// Boolean 결과값 확인
    assertNotNull			// 객체가 Null이 아닌지 확인
    assertArrayEquals		// 두 배열의 값이 같은지 확인

     

    테스트 코드를 작성하기 위해서는 생각보다 많은 지식이 필요한 것은 아닙니다.

    테스트 코드를 작성하기 어려운 프로젝트는 이미 개발이 많이 되어 있으며 모듈(함수/클래스) 단위가 커서 테스트 코드를 작성하기 어려운 소스코드가 존재할 때가 오히려 테스트 작성이 어렵습니다.

    그렇기에 시작부터 프로그래밍을 하실 때는 항상 테스트코드를 생활화 하셔야겠습니다.

     

    Java code coverage의 약자로

    junit 테스트의 결과를 바탕으로 커버리지를 결과를 리포트 해주는 툴 입니다.

    특히 코드정적분석도구인 sonarqube와 연계하여 사용하는 경우가 많습니다.

     

    앞선 툴들에 대한 자세한 설명은 다른 포스팅에서 진행할 예정입니다. 이번 글은 제 프로젝트에 적용해볼려고 합니다.

     

    1. build.gradle에 jacoco 추가

    - plugins에 아래처럼 추가

    plugins {
    	...
    	id 'jacoco'
    }
    
    ...
    }

    추가한 뒤 gradle reload를 진행해주면 verification 에 아래 와 같이 추가됩니다.

    2. gradle test 진행

    - 위 사진의 test를 더블 클릭하셔도 되고 gradle test 명령어로 하셔도 됩니다.

    - test 진행이 완료 되면 build > test 폴더 안에 index.html을 발견하실 수 있습니다. 

    - 끌어다가 띄우고 크롬을 통해서 열면

    - 이런 화면을 확인 하실 수 있습니다. 이건 jacoco가 아니라 그냥 test 결과입니다.

    - 이제 jacoco 리포트를 확인해봅시다.

    3. jacocoTestReport 실행

    - jacocoTestReport를 더블클릭해서 실행해줍니다.

    - build > report > jacoco 폴더 안에서 index.html을 확인 하실 수 있습니다. 열어줍니다.

    5. 결과 확인

    - 원래 전체 결과가 나오는데 생각보다 참혹해서 ㅎㅎㅎ 깨달은 부분만 .... 후후 ..

    - 프로젝트 중에 mockito를 이용해서 단위 테스트를 진행하면서 신경을 제일 쓴 service 테스트인대 어디를 안타는 거지 했습니다....

    - 내용 확인을 해보니 아래처럼 예외 테스트가 부족했다는 것을 확인 하였습니다. 

    - 생각해보니 파일 업로드 등 파일 관련 처리를 s3로 옴기고 나서 테스트 코드를 추가하지 않았다는걸 알게되었습니다.

    (추가해야지 .....ㅠㅠㅠㅠ)

     

    - 27%? 이게 뭐지 라는 생각으로 확인 해보니 (참혹한 dto 테스트 커버리지 .....) 

    - Dto에 롬복을 통해서 @Data를 통해서 setter 생성하고 한번도 안써서 생긴 내용 이었습니다 .ㅎ (@Builder 짱짱맨.ㅎ)

    - dto 테스트를 추가하면

    import org.junit.jupiter.api.Test;
    
    import static org.assertj.core.api.Assertions.assertThat;
    import static org.junit.jupiter.api.Assertions.*;
    
    class ProjectResponseDtoTest {
    
        @Test
        void projectResponseDtoTest() {
            ProjectResponseDto projectResponseDto1 = new ProjectResponseDto();
            projectResponseDto1.setProjectId(1L);
            projectResponseDto1.setProjectTitle("이름");
            projectResponseDto1.setProjectContent("내용");
            projectResponseDto1.setProjectPostScript("추신");
            projectResponseDto1.setFileUrl("파일 주소");
            projectResponseDto1.setFileOriginName("파일 이름");
            projectResponseDto1.setProjectLink("프로젝트 링크");
            projectResponseDto1.setLevel(1);
            projectResponseDto1.setMemberId(1L);
    
            ProjectResponseDto projectResponseDto2 = ProjectResponseDto.builder()
                    .projectId(1L)
                    .projectTitle("이름")
                    .projectContent("내용")
                    .projectPostScript("추신")
                    .fileUrl("파일 주소")
                    .fileOriginName("파일 이름")
                    .projectLink("프로젝트 링크")
                    .level(1)
                    .memberId(1L)
                    .build();
    
            assertAll(
                    () -> assertThat(projectResponseDto1).isEqualTo(projectResponseDto2),
                    () -> assertThat(projectResponseDto1.toString())
                            .hasToString("ProjectResponseDto(projectId=1, projectTitle=이름, projectContent=내용," +
                                    " projectPostScript=추신, fileUrl=파일 주소, fileOriginName=파일 이름," +
                                    " projectLink=프로젝트 링크, level=1, memberId=1)")
            );
        }
    }

     

    - 테스트 커버리지가 올라가긴합니다만

     

    - 과연 dto를 테스트하는게 의미가 있는지 위와 같은 코드를 작성하는 게 의미가 있는지 등을 생각하게 되면서 jacoco 설정을 찾아보게 되었습니다.

     

    - 저의 경우 dto와 querydsl에 의해 생기는 Q클래스를 제외하도록 특정 디렉토리의 특정 클래스를 제외시키는 방법을 사용하였습니다.( 제가 사용한 방법과 다른 방법 등은 아래 주소에서 확인하실 수 있습니다.)

    // build.gradle 에 추가
    
    jacocoTestReport {
    	dependsOn test // tests are required to run before generating the report
    
    	afterEvaluate {
    		classDirectories.setFrom(files(classDirectories.files.collect {
    			fileTree(dir: it, exclude: [
    					"com/myintroduce/web/dto/*",
    					"**/Q*.class"
    			])
    		}))
    	}
    }

     

    (참고 : https://wellbell.tistory.com/242)

    + Recent posts