티스토리 뷰

웹/Spring

스프링 볶음밥 - 2장 - 테스트

세댕댕이 2022. 7. 2. 14:05

 

 

[이전글]

더보기

볶음밥 1장 - 1,2,3: 자바빈, 디자인 패턴(템플릿 메소드, 팩토리 메소드, 전략 패턴), 관심사의 분리, SOLID 및 객체지향 약간

볶음밥 1장 - 4,5,6: 제어의 역전, 프레임워크 vs 라이브러리, 스프링 IoC 및 용어 정리, 싱글톤, 동일성 vs 동등성, 빈의 스코프

볶음밥 1장 - 7: 의존관계 주입(DI), DL, IoC

 

[2장] 테스트

변화하는 애플리케이션에 효과적으로 대응할 수 있는 전략

1. IoC/DI 컨테이너를 이용해 확장과 변화를 고려한 객체지향적 설계

2. 만들어진 코드를 확신할 수 있게 해주고 변화에 유연하게 대처할 수 있도록 자신감을 주는 "테스트"

 

 

[2-1] UserDaoTest 다시보기

테스트 = 내가 의도한 대로 코드가 정확히 동작하는지를 확인해 내가 만든 코드를 확신할 수 있도록 해주는 작업.

이 과정을 통해 코드나 설계의 결함을 발견하고, 디버깅을 통해 결함을 제거한다.

 

 

[웹을 통한 DAO 테스트의 문제점]

웹을 통해 DAO를 테스트하기 위해서는 DAO 뿐만 아니라 서비스 계층, 프론트엔드 작업, 컨트롤러도 만들고 등등 부가적으로 해야하는 일이 너무나도 많다. 오히려 배보다 배꼽이 더 커지는 느낌이다. 또한 이는 DAO 고유의 테스트가 아니다.

테스트 역시 관심사의 분리를 통해 가능하면 작은 단위로 쪼개는 것이 바람직하다. 

-> 그래서 해야하는 것이 바로 "단위 테스트"(Unit Test)

 

 

[단위테스트]

- 일반적으로 단위는 작을수록 좋다

--> 여기서 단위의 범위는 상대적이다. 하나의 관심에 집중해서 효율적으로 테스트할 수 있는 범위의 단위.

- "통제할 수 없는" 외부의 리소스에 의존하는 테스트는 단위 테스트가 아니라고 볼 수도 있다 (= 썩 바람직하지는 않다)

- 단위테스트는 개발자가 설계하고 만든 코드가 원래 의도한 대로 동작하는지를 빨리 확인해보기 위해서이다.

- 테스트는 "자동"으로 수행되도록 하는 것이 중요하다 (Run만 해주면 따로 사용자 입력 없이 알아서 다 되야한다는 뜻)

--> 테스트는 자주 반복될 수 있기 때문

* 단위테스트는 항상 일관성 있는 결과가 보장되어야 한다

- DB에 남아있는 데이터 등 외부 환경에 영향을 받으면 안된다

- 테스트 실행 순서에 영향을 받으면 안된다

 

 

[2-2] UserDaoTest 개선

개선사항 = 테스트 검증의 자동화

- sout으로 찍어주는것은 내가 직접 출력 결과를 확인해보고 잘 됐는지 안됐는지를 확인해야한다

- 이것보다 그냥 테스트 성공 / 실패로 바로 알려주는 것이 훨씬 좋다. 테스트가 많아질수록 더더더욱..

 

 

[2-3] 테스팅 프레임워크 JUnit

자동화된 테스트를 도와주는 짱 프레임워크가 바로 jUnit(xUnit)!!

- jUnit도 프레임워크 = IoC 원리가 적용되어있다

 

토비의 스프링이 아무래도 오래된 책이다보니 프레임워크 버전이 좀 다른것 같긴 하다

그래서 난 이전에 사용했던 jUnit5로 사용하기로 했다. 스프링부트를 사용했을때는 테스트용 프레임워크까지 싹다 포함해주는데 스프링부트는 사용하지 않았기에 mavenRepository 사이트에서 프레임워크를 검색한 다음에 pom.xml에 추가해줘야한다. 

 

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.21</version>
    <scope>test</scope>
</dependency>
        
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.8.2</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-core</artifactId>
    <version>3.23.1</version>
    <scope>test</scope>
</dependency>

assertj는 assertThat()을 사용하기 위해 추가. 

스프링부트가 편한게 이런 dependency들을 스프링부트 스타터 한방에 만들어주니 내가 신경쓸 필요가 없다는 것이다

 

* 테스트를 안만드는 것도 위험하지만, 성의없는 테스트를 작성해 문제있는 코드를 테스트에 성공하게 만드는 것은 더더욱!!! 위험하다. 

* 성공하는 테스트 케이스보다 실패하는 테스트 케이스를 만드는 것이 더더더욱 중요하다!!

* TDD(Test Driven Development): 실패하는 테스트 코드를 먼저 만들고 나서 테스트를 성공하게 해주는 코드를 나중에 작성하는 개발 방법.

TDD의 장점: 오류의 유무를 가장 빠르게 알아낼 수 있음, 불안정성 해소.

TDD의 단점: 생산성 저하. 귀찮음

 

[JUnit의 기본적인 동작 방식]

1. 테스트 클래스에서 @Test가 붙은 public이고 void형이며 파라미터가 없는 테스트 메소드를 모두 찾는다

2. 테스트 클래스의 오브젝트를 하나 만든다

3. @Before(-> jUnit5에서 @BeforeEach로 변경됨)가 붙은 메소드가 있으면 먼저 실행한다

4. @Test가 붙은 메소드를 하나 호출하고 테스트 결과를 저장해둔다

5. @After(-> @AfterEach)가 붙은 메소드가 있으면 실행한다

6. 나머지 테스트 메소드들에 대해 2~5번을 반복

7. 저장된 테스트 결과들을 종합해 출력

 

* @Before-, @After- 는 테스트 메소드가 직접 호출하는 것이 아니기 때문에 서로 주고받아야할 오브젝트가 있다면 인스턴스 변수를 사용해야 한다!

* 각 테스트 메소드를 실행할 때마다, 테스트 클래스의 오브젝트를 새로 만든다

-> 한번 만들어진 테스트 클래스의 오브젝트는 하나의 테스트 메소드를 실행하고 버려진다.

-> 각 테스트가 서로 영향을 주지 않고 독립적으로 실행됨을 보장해준다

 

 

* 테스트 메소드의 일부에서만 공통적으로 사용되는 코드가 있다면 메소드를 추출해서 사용하는 편이 좋다.

* 픽스쳐(fixture): 테스트를 수행하는 데 필요한 정보나 오브젝트.

- 주로 여러 테스트에서 반복적으로 사용되는 경우가 많기에 @Before 메소드를 이용해 생성해두고 돌려쓰는게 좋다.

class UserDaoTest {

    // Fixture
    private UserDao dao;
    private User user1;
    private User user2;
    private User user3;
    ////
    
    @BeforeEach
    public void setUp() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(DaoFactory.class);
        this.dao = ac.getBean("userDao", UserDao.class);

        this.user1 = new User("id1", "name1", "pw1");
        this.user2 = new User("id2", "name2", "pw2");
        this.user3 = new User("id3", "name3", "pw3");
    }
...

 

[2-4] 스프링 테스트 적용

이것도 책이랑 버전차이가 조금 있다.

스프링부트를 사용하면 @SpringBootTest 애노테이션 하나만 클래스 위에 붙여주면 된다.

근데 난 지금 스프링부트를 안쓰고 하고 있는 중이었어서

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {DaoFactory.class})
class UserDaoTest {
....
}

이렇게 붙여줘야 한다. @ContextConfiguration 내 class 이름은 애플리케이션 컨텍스트의 위치를 지정해주면 된다.

 

 

[2-5] 학습 테스트

학습 테스트: 자신이 만들지 않은 프레임워크다 다른 개발자들이 만든 라이브러리 등에 대해서도 테스트 해보는 것

- 다양한 조건에 따른 기능을 손쉽게 확인 가능

- 호환성 검증을 도와준다

- 새로운 기술을 공부하는 좋은 방법 중 하나

 

버그 테스트: 코드에 오류가 있을때 그 오류를 가장 잘 드러내줄 수 있는 테스트

- 일단 테스트를 실패하도록 만든 다음 -> 테스트가 성공할 수 있도록 애플리케이션 코드를 수정하는 방법.

- 테스트의 완성도를 높여준다

- 버그를 효과적으로 분석할 수 있게 해준다.

- 기술적인 문제를 해결하는데 도움을 준다.

 

 

[결론]

1. 테스트는 자동화 되어야 하고, 빠르게 실행될 수 있어야 한다.

2. 테스트 결과는 일관성이 있어야 한다

3. 테스트는 포괄적으로 상세하게 작성되어야 한다. 충분한 검증 없는 테스트는 없는것보다 더 안좋을 수도 있다

4. 단위테스트를 생활화하자

5. 테스트 하기 쉬운 코드가 좋은 코드다

 

 

테스트 섹션은 아무래도 버전 차이도 나고 하다보니 빠르게 넘겼다..junit은 따로 공부하자..

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함