강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

윤소영님의 프로필 이미지
윤소영

작성한 질문수

스프링 핵심 원리 - 기본편

컨테이너에 등록된 모든 빈 조회

테스트 두개를 동시에 돌렸을 경우 왜 밸류값이 달라지는 건가요??

작성

·

102

0


public class ApplicationContextInfoTest {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("모든 빈 출력하기")
    void findAllBean() {
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            Object bean = ac.getBean(beanDefinitionName);
            System.out.println("name = " + beanDefinitionName + " Object = " + bean);
        }
    }
    @Test
    @DisplayName("모든 빈 출력하기")
    void findApplicationAllBean() {
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);

            if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
                Object bean = ac.getBean(beanDefinitionName);
                System.out.println("name = " + beanDefinitionName + " Object = " + bean);
            }
        }
    }

}

테스트 코드를 이렇게 짠 후 같이 돌리면 오브젝트로 나오는 애들 코드가 각각 다르게 나옵니다.

스프링 컨테이너는 초기에 한 번만 생성/등록되고 각각의 테스트에서 같은 것을 가져오는 건줄 알았는데 아닌가요..??

답변 2

1

제가 스프링은 아직 배워가는 단계라 답변이 정확하지는 않을 수도 있으나 제가 이해한 내용을 기반으로 설명드리겠습니다.

 

(config 파일에 @Configuration 애노테이션을 달았다는 가정하에) 스프링 컨테이너는 질문자님이 말씀하신 것처럼 싱글톤으로 작동하는 것이 맞습니다.

다만, 각 Test는 독립적으로 실행되기 때문에 다음과 같이 진행됩니다.

  1. 각 테스트마다 현재 테스트에 대한 테스트 클래스의 인스턴스 생성 이 과정에서 field(컨테이너)도 당연히 초기화
    (이때 컨테이너는 싱글톤 컨테이너가 맞음)

  2. 해당 테스트 진행

  3. 해당 테스트 완료시 사용한 인스턴스 정리
    (따라서 다음 테스트에서는 새롭게 컨테이너를 초기화)

     
    (즉 다음 테스트에서는 컨테이너 자체가 다른 컨테이너이고, bean도 새로 생성해서 넣기 때문에 그 참조도 당연히 다름)

따라서 동일한 이름의 bean이지만 각 테스트가 시작할 때 새로 생성되고, 테스트 종료시에 제거되기 때문에 그 참조가 다르게 출력되는 것입니다.

따라서 컨테이너를 인스턴스 변수가 아니라 static으로 선언하여 클래스 수준에서 유지하면 모든 테스트에서 동일한 컨테이너를 사용하기 때문에 싱글톤 컨테이너에 의해 객체 참조가 동일함이 보장됩니다.

static AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

 

답변하다가 추가적으로 궁금한 부분이 생겨 챗지피티에게 물어보니

"지금은 자바 코드로 직접 테스트하고 있지만, @SpringBootTest 를 통해서 스프링 부트를 통해 테스트를 하도록 하면 내부적으로 캐시를 사용하여 각 테스트에서 동일한 컨테이너와 빈 인스턴스를 사용합니다."

라고 답변을 해 주네요. 그런데 이 부분은 제가 아직 학습하지 않아서 설명은 못드리겠고, 추가적으로 학습하실 때 도움이 될까 해서 글로 남깁니다.

// 반대로, Spring 테스트를 제대로 쓰면?
@SpringBootTest
class MyTest {

    @Autowired
    ApplicationContext ac;

    @Test
    void test1() {
        Object bean = ac.getBean("memberService");
        System.out.println(System.identityHashCode(bean));
    }

    @Test
    void test2() {
        Object bean = ac.getBean("memberService");
        System.out.println(System.identityHashCode(bean)); // 위와 동일한 hashCode
    }
}
// 이렇게 하면 두 테스트 모두 같은 컨테이너, 같은 빈 인스턴스를 사용합니다.
// 이건 스프링이 ApplicationContext 캐시를 사용하기 때문입니다.

0

안녕하세요. 윤소영님, 공식 서포터즈 David입니다.

아래 테스트는 if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) { 조건으로 필터링하여 출력하고 있는 점을 참고해 주세요:)

감사합니다.

윤소영님의 프로필 이미지
윤소영

작성한 질문수

질문하기