본 게시글은 테스트 코드만을 다룬다. 이미 작성된 코드들 (AppConfig
, MemberService
, MemberServiceImpl
등)은 이전 게시글을 참고하면 된다.
또한, JUnit 프레임워크를 통해 테스트 코드를 작성하므로, 관련 지식이 부족하다면 자료를 찾아보시길 바란다.
기본적인 방법으로 조회
getBean() 메서드
빈 이름으로 조회하기
getBean(빈이름, 빈타입)
- 빈이름: 조회할 빈의 이름을 뜻한다.
- 빈타입: 조회할 빈의 타입을 뜻한다. 다형성을 활용하기 위해 보통 인터페이스 타입으로 조회한다.
import hello.core.AppConfig;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.*;
class ApplicationContextBasicFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("빈 이름으로 조회")
void findBeanByName() {
//빈 이름으로 조회하기
MemberService memberService = ac.getBean("memberService", MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
}
빈 타입만으로 조회하기
getBean(빈타입)
- 해당 타입을 가지는 빈 객체를 반환한다.
- 타입으로 조회시 같은 타입의 스프링 빈이 둘 이상이면 오류가 발생한다.
- 따라서, 빈이름을 지정해야한다.
import hello.core.AppConfig;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.*;
class ApplicationContextBasicFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("이름없이 타입만으로 조회")
void findBeanByType() {
//빈 타입만으로 조회하기
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
}
빈 구체 타입으로 조회하기
getBean(빈이름, 구체타입)
- 인터페이스 타입이 아닌 구체 타입으로 조회한다.
- 보통 이런 방식으로 사용하지는 않는다.
- 왜냐하면, 다형성을 사용할 수 없기 때문에 변경시 유연성이 떨어지기 때문이다.
import hello.core.AppConfig;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.*;
class ApplicationContextBasicFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("이름과 구체 타입으로 조회")
void findBeanByName2() {
//빈 구체타입으로 조회하기
MemberService memberService = ac.getBean("memberService", MemberServiceImpl.class);
assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
}
존재하지 않는 빈 조회하기
NoSuchBeanDefinitionException
예외- 존재하지 않는 빈 이름을 조회하면 해당 예외가 발생한다.
import hello.core.AppConfig;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.*;
class ApplicationContextBasicFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
@Test
@DisplayName("존재하지 않는 빈 조회")
void findBeanByNameX() {
//빈 조회
Assertions.assertThrows(NoSuchBeanDefinitionException.class ,
() -> ac.getBean("Xxxx", MemberService.class) );
}
}
동일한 타입이 둘 이상일때 조회
전제
-
MemberRepository
라는 인터페이스의 구현 클래스가MemoryMemberRepository
클래스와DbMemberRepository
클래스라고 하자. MemoryMemberRepository
- memberRepository1 라는 이름으로 빈 등록되었다고 가정한다.
DbMemberRepository
- memberRepository2 라는 이름으로 빈 등록되었다고 가정한다.
@Configuration
class SameBeanConfig {
@Bean
public MemberRepository memberRepository1() {
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2() {
return new DbMemberRepository();
}
}
getBean() 메서드
타입만으로 조회하기
NoUniqueBeanDefinitionException
예외- 타입만으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류가 발생한다.
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
class ApplicationContextSameBeanFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 중복 오류가 발생한다")
void findBeanByTypeDuplicate() {
//예외 발생 코드
//DiscountPolicy bean = ac.getBean(MemberRepository.class);
assertThrows(NoUniqueBeanDefinitionException.class, () ->
ac.getBean(MemberRepository.class)); //인터페이스 타입으로 조회
}
}
빈 이름, 빈 타입으로 조회하기
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
class ApplicationContextSameBeanFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);
@Test
@DisplayName("타입으로 조회시 같은 타입이 둘 이상 있으면, 빈 이름을 지정하면 된다.")
void findBeanByName() {
MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
assertThat(memberRepository).isInstanceOf(MemberRepository.class);
}
}
getBeansOfType() 메서드
특정 타입을 모두 조회하기
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
class ApplicationContextSameBeanFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SameBeanConfig.class);
@Test
@DisplayName("특정 타입을 모두 조회하기")
void findAllBeanByType() {
Map<String, MemberRepository> beansOfType =
ac.getBeansOfType(MemberRepository.class);
// beansOfType 에 담긴 모든 요소 출력
for(String key : beansOfType.keySet()) {
System.out.println("key = " + key + " value = " + beansOfType.get(key));
}
assertThat(beansOfType.size()).isEqualTo(2);
}
}
상속 관계 조회
- 부모 타입으로 조회하면, 자식 타입도 함께 조회한다.
Object
타입으로 조회하면, 모든 스프링 빈을 조회하게 된다.Object
는 모든 자바 객체의 최고 부모이기 때문이다.
전제
MemberRepository
라는 인터페이스의 구현 클래스가MemoryMemberRepository
클래스와DbMemberRepository
클래스라고 하자.MemoryMemberRepository
- memberRepository1 라는 이름으로 빈 등록되었다고 가정한다.
DbMemberRepository
- memberRepository2 라는 이름으로 빈 등록되었다고 가정한다.
@Configuration
class SameBeanConfig {
@Bean
public MemberRepository memberRepository1() {
return new MemoryMemberRepository();
}
@Bean
public MemberRepository memberRepository2() {
return new DbMemberRepository();
}
}
부모 타입으로 조회
타입으로만 조회 with getBean()
- 부모 타입으로 조회시, 자식이 둘 이상 있으면, 중복 오류가 발생한다.
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
class ApplicationContextExtendsFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 중복 오류가 발생한다")
void findBeanByParentTypeDuplicate() {
//DiscountPolicy bean = ac.getBean(DiscountPolicy.class);
assertThrows(NoUniqueBeanDefinitionException.class, () ->
ac.getBean(DiscountPolicy.class));
}
}
빈 이름과 타입으로 조회 with getBean()
- 부모 타입으로 조회시, 자식이 둘 이상 있으면, 빈 이름까지 지정하면 된다.
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
class ApplicationContextExtendsFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 이름 지정하면 된다.")
void findBeanByParentTypeBeanName() {
MemberRepository memberRepository = ac.getBean("memberRepository1", MemberRepository.class);
assertThat(memberRepository).isInstanceOf(MemberRepository.class);
}
}
부모 타입으로 모두 조회 with getBeansOfType()
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
class ApplicationContextExtendsFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 이름 지정하면 된다.")
void findAllBeanByParentType() {
//MemberRepository 타입을 갖는 모든 빈 객체를 Map으로 반환받음
Map<String, MemberRepository> beansOfType = ac.getBeansOfType(MemberRepository.class);
//beansOfType에 들어있는 모든 요소 출력
for(String key : beansOfType.keySet) {
System.out.println("key = " + key + " value = " + beansOfType.get(key));
}
assertThat(beansOfType.size()).isEqualTo(2);
}
}
특정 하위 타입으로 조회
특정 하위 타입으로 조회 with getBean()
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
class ApplicationContextExtendsFindTest {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면, 이름 지정하면 된다.")
void findBeanBySubType() {
//MemoryMemberRepository 타입으로 조회함
MemberRepository memberRepository = ac.getBean("memberRepository1", MemoryMemberRepository.class);
assertThat(memberRepository).isInstanceOf(MemberRepository.class);
}
}
- 본 게시글은 김영한님의 강의를 토대로 정리한 글입니다.
- 더 자세한 내용을 알고 싶으신 분들이 계신다면, 해당 강의를 수강하시는 것을 추천드립니다.