Group Study (2022-2023)/Spring 심화

[Spring 심화] 2주차 스터디 - 2장 테스트

dusdb 2022. 10. 10. 21:36

테스트란?

테스트는 예상하고 의도했던 대로 코드가 정확히 동작하는지 확인해서, 만든 코드를 확신할 수 있게 해주는 작업으로

코드로 테스트가 수행되도록 하므로 자주 반복할 수 있고, 코드에 새로운 변경이 생겼을때 기존의 기능에 문제가 있는지 없는지 빨리 확인할 수 있다.

 

 

단위 테스트

작은 단위의 코드에 대해 테스트를 수행한것

작은 단위로 쪼개서 자신의 관심사에만 집중해서 테스트

 

 단위 테스트의 장점

각 작은 단위 별로 테스트하므로 오류의 원인을 찾기 쉽다.

확인의 대상과 조건이 간단할수록 좋다.

 

JUnit

자바 테스팅 프레임워크, 자바를 단위 테스트로 만들때 유용하게 사용한다.

많은 테스트를 간단히 실행, 테스트 종합결과 확인, 테스트 실패한곳 빠르게 확인 등의 기능 제공한다.

메소드는 public으로 선언되어야 하고, 메소드에 @Test 애노테이션을 붙여야 한다.

메소드 이름은 테스트의 의도를 알 수 있도록 한다.

 

JUnit 테스트 실행 방법

  1. 자바 IDE(EX. 이클립스)에 내장된 테스트 지원도구 사용하기                                                                                        간단한 단축키로 원하는 클래스의 테스트를 할 수 있고 테스트 결과를 간단하고 직관적으로 소스와 연동해서 볼 수 있다. 혼자 작업할때는 이 지원도구를 이용하자.
  2. 빌드 툴에서 제공하는 지원도구 사용하기                                                                                                                       여러 개발자가 만든 코드를 모두 통합해서 테스트를 수행해야 할 경우 서버에서 모든 코드를 가져와 통합하고 빌드한 뒤에 테스트를 수행하는것이 좋은데, 이 경우 빌드 스크립트를 이용해 테스트 진행

 

테스트가 실패한다면 실패한 원인과 실패한 위치를 알려준다. 그렇다면 여기서는 무조건 이 에러가 발생해야해! 하는 테스트도 있을 것이다. JUnit은 다음과 같은 방법도 제공해준다. 

 

예외조건 테스트

@Test에 발생할 것으로 기대되는 예외 클래스를 지정해주면 테스트 진행중에 특정 예외가 던져지면 테스트 성공, 아니면 실패한다.

테스트는 실행 순서, 외부 상태에 상관없이(EX. DB에 남아있는 데이터) 코드에 변경이 없다면 항상 일관성 있는 결과를 내야한다. 따라서 테스트하기 전에 테스트 실행에 문제가 되지 않는 상태를 만들어주어야 한다.

 

JUnit이 테스트 메소드를 실행할때 부가적으로 해주는 기능

@Test가 붙은 메소드를 실행하기 전과 후에 각각 @Before, @After가 붙은 메소드를 자동으로 실행한다.

공통적인 준비 작업과 정리 작업을 이 메소드에 넣어주면 중복도 없고 자동으로 실행해주기 떄문에 편리하다.

 

JUnit이 하나의 테스트 클래스를 가져와 테스트를 수행하는 방식

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

2. 테스트 클래스의 오브젝트를 하나 만든다.(각 테스트 메소드마다 테스트 클래스의 오브젝트를 만든다.)

3. @Before가 붙은 메소드가 있으면 실행한다.

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

5. @After가 붙은 메소드가 있으면 실행한다.

6. 나머지 테스트 메소드에 대해 2~5를 반복한다.

7. 모든 테스트의 결과를 종합해서 돌려준다.

 

❓ 왜 각 테스트 메소드마다 테스트 클래스의 오브젝트를 만들까? 

각 테스트가 서로 영향을 주지 않고 독립적으로 실행됨을 확실히 보장해 주기 위해서이다. 어처피 매 테스트 메소드마다 새로운 오브젝트가 만들어 질것이니 인스턴스 변수도 부담없이 사용할 수 있다.

 

❓ 생성에 많은 시간과 자원이 소모되는 오브젝트도 매번 생성?

테스트는 매번 새로운 오브젝트를 사용하는게 원칙이다. 그러나 애플리케이션 컨텍스트는 생성에 많은 시간과 자원이 소모되므로 테스트 전체가 공유하는 오브젝트를 만들기도 하는데 이때 @BeforeClass 필드에 애프리케이션 컨텍스트를 저장해두면 컨텍스트는 한번만 만들고 여러 테스트가 공유해서 사용하게 된다. 하나의 컨텍스트로 공유해서 쓰면 재사용이 가능하기 때문에 테스트 성능이 향상된다.

@BeforeClass 
테스트 클래스 전체에 걸쳐 한번만 실행되는 JUnit 스태틱 메소드

 

테스트 주도 개발

코드를 먼저 짜고 테스트코드를 만들면 이 코드가 잘 돌아가는 케이스만 상상하며 성공하는 테스트만 골라서 만든다.

테스트를 작성할떄 부정적인 케이스, 실패할 케이스를 먼저 만들어 예외적인 상황까지 테스트할 수 있도록 한다.

 

위 문제를 잘 해결하고자 나온 방법이 "테스트 주도 개발"이다.

 

만들고자 하는 기능의 테스트 코드를 먼저 만들고, 테스트를 성공하기 위한 코드를 작성하는 방법으로

코드를 만들어 테스트를 실행하는 사이 간격이 매우 짧아 오류를 빨리 발견할 수 있고 위 개발방식을 따라서 개발한다면 모든 코드가 테스트로 검증되게 된다는 장점이 있다.

 

스프링 테스트 컨텍스트 프레임워크

스프링이 제공하는 기능으로, 간단한 애노테이션 설정만으로 테스트에서 필요로 하는 애플리케이션 컨텍스트를 만들어서 모든 테스트가 공유하게 할 수 있다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="/applicationContext.xml)
public class ---Test{

	@Autowired
	private ApplicationContext context;
    
    }

@RunWith는 JUnit 프레임워크의 테스트 실행 방법을 확장할 때 사용하는 애노테이션으로, 테스트를 진행하는 중에 테스트가 사용할 애플리케이션 컨텍스트를 만들고 관리하는 작업을 한다.

 

@ContextConfiguration은 자동으로 만들어줄 애플리케이션 컨텍스트의 설정파일 위치를 지정한 것이다.

다른 클래스가 같은 설정파일을 사용하는 경우 다른 테스트 클래스 간에도 하나의 애플리케이션 컨텍스트를 공유한다.

 

@Autowired가 붙은 인스턴스 변수가 있으면, 변수타입과 일치하는 컨텍스트 내의 빈을 찾아 주입해준다.

 context변수를 따로 초기화 해주지 않아도 테스트 실행 전 하나의 애플리케이션 컨텍스트를 만들고, 각 테스트 오브젝트가 만들어지고 난 후 해당 필드에 자동으로 값(애플리케이션 컨텍스트)을 주입해준다.

 

 

DI와 테스트

DI를 통해 외부에서 사용할 오브젝트를 주입받는 식으로 테스트코드를 짜면 오브젝트 생성에 대한 부담이 줄어들고 코드의 수정 없이 의존 오브젝트를 바꿔가며 사용할 수 있다. 이때 가능한 한 인터페이스를 사용해서 애플리케이션 코드와 느슨하게 연결하는것이 좋다. 만약 수정할일이 생길시 느슨하게 연결되어 있어 부담이 줄어든다.

 

  • 애플리케이션 컨텍스트 없이 테스트

스프링 컨테이너 없이 테스트하는 방식으로 애플리케이션 컨텍스트를 만들지 않고 직접 오브젝트를 만들고 DI해서 사용한다. 테스트가 간결하고 수행 속도가 빠르다. 테스트를 위해 필요한 오브젝트의 생성과 초기화가 단순하다면 제일 먼저 이 방법을 고려해보자.

 

  • 애플리케이션 컨텍스트를 이용하여 테스트

여러 오브젝트와 복잡한 의존관계를 갖고 있는 경우에는 스프링의 설정을 이용한 DI를 사용하자. 

 

1) 테스트 전용 설정파일을 따로 만들어 적용

테스트용 설정파일을 따로 만들어서 테스트는 해당 설정파일 정보를 이용하는 애플리케이션 컨텍스트를 사용할 수 있도록 한다.  

 

2) 테스트를 설정하더라도 예외적인 의존관계를 강제로 구성할 경우

컨텍스트에서 받은 오브젝트에 다시 수동DI해준다. 이 경우 애플리케이션 컨텍스트의 상태를 변경하므로 @DirtiesContext 애노테이션을 붙여 해당 애노테이션이 붙은 테스트는 애플리케이션 공유를 허용하지 않도록 한다.