콘텐츠로 이동

2021 05 16

2021-05-16

토비의 스프링 2단원

핵심 내용

  • 객체 지향 기술과 더불어 다른 하나의 도구로 스프링이 강조하고 가치를 두는 것은 "테스트"
  • 기존 테스트 + 수동 테스트의 문제점 - 1단원에서 작성한 테스트는 main() 메서드를 통해 테스트를 수행함... 단점이 많음 - 수동 확인 작업 필요 - 실행 작업 번거로움 - 제어권을 직접 가진다는 것이 프레임워크의 컨셉에 안어울림 - 웹 화면을 통한 테스트는 값 입력 -> 기능 수행 -> 결과 확인... 너무 번거로움 - 모든 레이어 다 만들어야 테스트 가능하다는 단점
  • 단위 테스트 + 자동 테스트 - 테스트하고자 하는 대상이 명확하다면, 그 대상에만 집중하여 테스트하는 것이 바람직하다 - 작은 단위의 코드에 대한 테스트를 "단위 테스트"라고 한다 - 단위 테스트는 항상 일관성 있는 결과가 보장 되어야 함 - DB에 남아 있는 데이터와 외부 환경에 영향 받지 않아야 함 - 테스트 실행 순서 바뀌어도 동일한 결과여야 함 - 테스트는 자동으로 수행되도록 코드로 만드는 것이 중요하다 - 어떤 개발자는 모든 클래스가 스스로 테스트 하는 main() 메서드를 가지고 있어야 한다고 주장 - 어찌되었든, 코드가 만들어져 자동으로 수행될 수 있어야 하는 것은 매우 중요! - 자동 테스트는 자주 반복하기 용이함
  • 테스트의 장점 - 단순 무식한 방법으로 정상 동작하는 코드 만들고, 테스트를 통해 점진적 리팩토링 가능 - 최종적으로 모든 테스트가 성공하면, 새로 추가한 기능도 정상적으로 동작하고 기존 기능에 영향 안 줌 확인 가능
    - "항상 네거티브 테스트를 먼저 만들 것" - 성공할 거 알고 짠 테스트는 무의미
  • TDD - 선 테스트, 후 프로덕션 - 테스트를 빼먹지 않고 꼼꼼하게 만들 수 있음 - 테스트 ~ 프로덕션 작성 시간 Short - 테스트 덕에 오류 빨리 잡아내서 개발 속도 Fast
  • Junit - 테스트 수행 방식 1. @Test + public void 형의 테스트 메서드 찾기 2. 테스트 클래스 "오브젝트 생성" 3. @Before 붙은 메서드 실행 4. @Test 붙은 메서드 실행 5. @After 붙은 메서드 실행 - 각각의 테스트 메서드 실행할 때마다 테스트 클래스의 오브젝트 새로 만든다! - 한 번 만들어진 테스트 클래스의 오브젝트는 하나의 테스트 메서드 사용하고 난 후 버려짐 - 각 테스트가 서로 영향 주지 않고 독립적으로 실행함을 보장하려고 이렇게 설계
  • 테스트를 위한 애플리케이션 컨텍스트 관리 - 애플리케이션 컨텍스트는 생성에 많은 시간이 소모됨 - 원칙적으론 매 테스트 마다 새로 생성해야 하지만, 이를 공유하는 경우도 있음 - 테스트에서 사용할 설정파일을 지정해두고, @Autowired를 통해 주입시킬 수 있음
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = DaoFactory.class)
    public class UserDaoTest {
    
        @Autowired
        private UserDao dao;
    }
    

    - 테스트에서도 인터페이스를 사용해 애플리케이션 코드와 느슨하게 연결해두자 - @DirtiesContext - 스프링의 테스트 컨텍스트 프레임워크에게 해당 클래스의 테스트에서 애플리케이션 컨텍스트의 상태 변경한다는 것을 알려줌 - 해당 어노테이션이 붙으면 애플리케이션 컨텍스트 공유를 허용하지 않음 - 따라서 뒤에 테스트 영향 X - 테스트 방법 선택 시 고려할 사항 1. 스프링 컨테이너 없이 테스트 가능하다면 그렇게 하자 2. 테스트에서 애플리케이션 컨텍스트 사용한다면 테스트 전용 설정 파일을 만들어두자 3. 예외 적인 의존 관계를 강제로 구성해야 한다면... - 컨텍스트에서 DI 받은 오브젝트에 다시 테스트 코드로 수동 DI 해서 테스트하도록 하자

추가 공부 부분

  • Configuration에 대한 공부가 하나도 안되어 있음
  • @DirtiesContext에 대해서도 공부할 것

방화벽?

  • 참고: https://ko.wikipedia.org/wiki/%EB%B0%A9%ED%99%94%EB%B2%BD_(%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%82%B9)
  • 정의 - 미리 정의된 보안 규칙에 기반한, 들어오고 나가는 네트워크 트래픽을 모니터링하고 제어하는 NW 보안 시스템 - 신뢰할 수 있는 내부 네트워크 & 신뢰할 수 없는 외부 네트워크간의 장벽을 구성 - 외부로부터 내부망을 보호하기 위한 NW 구성요소의 하나 - 외부의 불법 침입으로부터 내부의 정보 자산을 보호 - 외부로 부터 불법 정보 유입을 차단하기 위함

@DirtiesContext?

  • 정의 & 필요성 - @DirtiesContext를 통해 테스트를 수행 전, 수행 후, "각 테스트마다 context를 재생성"하도록 지시할 수 있음! - 단독으로 잘 돌던 테스트가 한번에 돌리면 뇌절 - Spring test에서 다 같은 context를 사용해서 그런 걸 수도! - 각 Context를 생성하는 것이 아니라, 기존의 Context를 재활용 - 앞선 테스트에서 특정 Bean의 속성을 바꾸고, 제거하고, 추가해서 이후 테스트에 영향
  • 사용법 - 메서드나 클래스 레벨에 어노테이션 붙여 동작
    // 클래스 레벨
    @DirtiesContext(classMode = BEFORE_CLASS) //클래스의 테스트 시작하기 전 context 재생성
    @DirtiesContext //클래스의 테스트 모두 끝난 다음 context 재생성
    @DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD) // 클래스의 모든 테스트 케이스마다 시작하기 이전에 context 재생성
    @DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) // 클래스의 모든 테스트 케이스가 끝날 때 마다 context 재생성
    
    // 메서드 레벨
    @DirtiesContext(methodMode = BEFORE_METHOD) // 특정 케이스를 시작하기 전에 context 재생성
    @DirtiesContext // 특정 케이스 시작한 이후 context 재생성
    
  • 궁금증 - 지금 그래서 테스트 코드를 돌리면 어플리케이션 컨텍스트(빈 팩토리)가 몇 개 생긴다는거야? - 스프링 부트 테스트는 뭐지?

@SpringBootTest?

  • 참고: https://meetup.toast.com/posts/124
  • 필요성 - Spring Boot 애플리케이션을 테스트하기 위해 많은 기능 제공 - 테스트 모듈은 다음이 존재 - spring-boot-test - Junit, AssertJ, Hamcrest, SpringTest & SpringBootTest, Mockito, JSONassert, JsonPath 등 - spring-boot-test-autoconfigure
  • @SpringBootTest - 테스트에 사용할 ApplicationContext를 쉽게 생성/조작 - 기존 Spring-test의 @ContextConfiguration의 발전판 - 웹 환경 테스트 가능 - MOCK, RANDOM_PORT, DEFINED_PORT, NONE - TestRestTemplate... client-side의 웹 통합 테스트 지원 - RestTemplate의 테스트 버전 - 특정 빈 Mock 가능 - 테스트에 사용할 빈 쉽게 생성
  • @JsonTest - ObjectMapper와 @JsonComponent빈을 포함한 Jackson의 테스트를 위한 모듈 자동 설정
  • @WebMvcTest - server-side의 API 테스트 - MockMvc 관련 설정 자동 수행
  • @JdbcTest - 순수 JDBC 테스트 - in-memory embedded db 설정
  • 내가 했던 게 뭐였냐면 - 우선 DirtiesContext를 보면 클래스의 모든 테스트 케이스가 시작하기 이전에 Context를 새로 생성하네 - 이래서 오래 걸렸구나
    @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class AcceptanceTest {
        @LocalServerPort
        int port;
    
        @BeforeEach
        public void setUp() {
            RestAssured.port = port;
        }
    }