양식에서 유효성을 검사하기 위해 프로그램에서 사용되는 간단한 Bean에 대한 단위 테스트를 작성하려고합니다. Bean은 주석이 달린 @Component
클래스 변수를 사용하여 초기화됩니다.
@Value("${this.property.value}") private String thisProperty;
이 클래스 내에서 유효성 검사 메소드에 대한 단위 테스트를 작성하고 싶지만 가능하면 특성 파일을 사용하지 않고 수행하고 싶습니다. 이 뒤에 내 이유는 속성 파일에서 가져 오는 값이 변경되면 테스트 케이스에 영향을 미치지 않기를 원한다는 것입니다. 내 테스트 사례는 값 자체가 아닌 값의 유효성을 검사하는 코드를 테스트하는 것입니다.
테스트 클래스 내에서 Java 코드를 사용하여 Java 클래스를 초기화하고 해당 클래스 내부의 Spring @Value 속성을 채운 다음 테스트에 사용하는 방법이 있습니까?
이 How To 가 가깝다는 것을 알았지 만 여전히 속성 파일을 사용합니다. 오히려 그것은 모두 Java 코드입니다.
답변
가능하다면 Spring Context없이 테스트를 작성하려고합니다. 스프링없이 테스트에서이 클래스를 작성하면 해당 필드를 완전히 제어 할 수 있습니다.
@value
필드 를 설정하기 위해 Springs를 사용할 수 있습니다 ReflectionTestUtils
– setField
개인 필드를 설정 하는 방법 이 있습니다.
@ JavaDoc 참조 : ReflectionTestUtils.setField (java.lang.Object, java.lang.String, java.lang.Object)
답변
Spring 4.1부터 org.springframework.test.context.TestPropertySource
Unit Tests 클래스 레벨의 주석을 사용하여 코드에서 속성 값을 설정할 수 있습니다. 종속 Bean 인스턴스에 특성을 주입하는 경우에도이 방법을 사용할 수 있습니다.
예를 들어
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FooTest.Config.class)
@TestPropertySource(properties = {
"some.bar.value=testValue",
})
public class FooTest {
@Value("${some.bar.value}")
String bar;
@Test
public void testValueSetup() {
assertEquals("testValue", bar);
}
@Configuration
static class Config {
@Bean
public static PropertySourcesPlaceholderConfigurer propertiesResolver() {
return new PropertySourcesPlaceholderConfigurer();
}
}
}
참고 :org.springframework.context.support.PropertySourcesPlaceholderConfigurer
Spring 컨텍스트에 인스턴스가 있어야합니다.
24-08-2017 편집 : SpringBoot 1.4.0 이상을 사용하는 경우 테스트 @SpringBootTest
및 @SpringBootConfiguration
주석을 사용하여 테스트를 초기화 할 수 있습니다 . 더 많은 정보는 여기에
SpringBoot의 경우 다음 코드가 있습니다
@SpringBootTest
@SpringBootConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@TestPropertySource(properties = {
"some.bar.value=testValue",
})
public class FooTest {
@Value("${some.bar.value}")
String bar;
@Test
public void testValueSetup() {
assertEquals("testValue", bar);
}
}
답변
리플렉션으로 개인 필드를 가져 오거나 설정하지 마십시오.
여기에 몇 가지 답변에서 반사를 사용하면 피할 수 있습니다.
여기에는 작은 단점이 있지만 여러 가지 단점이 있습니다.
- 런타임시에만 반사 문제를 감지합니다 (예 : 더 이상 존재하지 않는 필드)
- 우리는 캡슐화를 원하지만 보이지 않는 종속성을 숨기고 클래스를 더 불투명하고 테스트하기 어려운 불투명 클래스는 아닙니다.
- 나쁜 디자인을 장려합니다. 오늘은을 선언합니다
@Value String field
. 내일 당신은 그 클래스에서5
또는10
그것들을 선언 할 수 있으며 , 당신은 클래스의 디자인을 줄인다는 것을 정확히 알지 못할 수도 있습니다. 이러한 필드 (생성자와 같은)를 설정하는보다 가시적 인 접근 방법을 사용하면 이러한 필드를 모두 추가하기 전에 두 번 생각하고 다른 클래스로 캡슐화하여 사용할 것@ConfigurationProperties
입니다.
수업을 단일 및 통합 테스트 가능
일반 단위 테스트 (스프링 컨테이너가 실행 중이 아님)와 Spring 컴포넌트 클래스에 대한 통합 테스트를 모두 작성하려면이 클래스를 Spring과 함께 또는없이 사용할 수 있어야합니다.
필요하지 않을 때 단위 테스트에서 컨테이너를 실행하면 로컬 빌드 속도가 느려지는 나쁜 습관이 있습니다.
나는이 대답을 추가하지 않았기 때문에이 대답을 추가했습니다. 따라서이 컨테이너는 체계적으로 실행중인 컨테이너에 의존합니다.
따라서이 속성을 클래스 내부로 정의해야한다고 생각합니다.
@Component
public class Foo{
@Value("${property.value}") private String property;
//...
}
Spring에 의해 주입 될 생성자 매개 변수에 :
@Component
public class Foo{
private String property;
public Foo(@Value("${property.value}") String property){
this.property = property;
}
//...
}
단위 테스트 예
Foo
Spring없이 인스턴스화 property
하고 생성자 덕분에 값을 삽입 할 수 있습니다 .
public class FooTest{
Foo foo = new Foo("dummyValue");
@Test
public void doThat(){
...
}
}
통합 테스트 예
다음과 같은 properties
속성 덕분에 Spring Boot를 사용하여 컨텍스트에 속성을 주입 할 수 있습니다 @SpringBootTest
.
@SpringBootTest(properties="property.value=dummyValue")
public class FooTest{
@Autowired
Foo foo;
@Test
public void doThat(){
...
}
}
대안으로 사용할 수 @TestPropertySource
있지만 추가 주석이 추가됩니다.
@SpringBootTest
@TestPropertySource("property.value=dummyValue")
public class FooTest{ ...}
Spring (Spring Boot없이)을 사용하면 조금 더 복잡해야하지만 오랫동안 Spring Boot없이 Spring을 사용하지 않았기 때문에 바보 같은 말을 선호하지 않습니다.
참고 사항 : @Value
설정할 필드 가 많은 경우 주석이 달린 클래스로 필드를 추출하는 @ConfigurationProperties
것이 더 관련이 있습니다. 인자가 너무 많은 생성자를 원하지 않기 때문입니다.
답변
원하는 경우 Spring 컨텍스트 내에서 테스트를 계속 실행하고 Spring 구성 클래스 내에서 필수 특성을 설정할 수 있습니다. JUnit을 사용하는 경우 SpringJUnit4ClassRunner를 사용하고 다음과 같이 테스트 전용 구성 클래스를 정의하십시오.
테스트중인 수업 :
@Component
public SomeClass {
@Autowired
private SomeDependency someDependency;
@Value("${someProperty}")
private String someProperty;
}
테스트 클래스 :
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SomeClassTestsConfig.class)
public class SomeClassTests {
@Autowired
private SomeClass someClass;
@Autowired
private SomeDependency someDependency;
@Before
public void setup() {
Mockito.reset(someDependency);
@Test
public void someTest() { ... }
}
이 테스트의 구성 클래스는 다음과 같습니다.
@Configuration
public class SomeClassTestsConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer properties() throws Exception {
final PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
Properties properties = new Properties();
properties.setProperty("someProperty", "testValue");
pspc.setProperties(properties);
return pspc;
}
@Bean
public SomeClass getSomeClass() {
return new SomeClass();
}
@Bean
public SomeDependency getSomeDependency() {
// Mockito used here for mocking dependency
return Mockito.mock(SomeDependency.class);
}
}
나는이 접근법을 권장하지 않는다고 말하면서 참조를 위해 여기에 추가했습니다. 내 의견으로는 훨씬 더 좋은 방법은 Mockito 러너를 사용하는 것입니다. 이 경우 Spring 내에서 테스트를 전혀 실행하지 않으므로 훨씬 명확하고 간단합니다.
답변
여전히 조금 장황하지만 작동하는 것 같습니다 (여전히 더 짧은 것을 원합니다).
@BeforeClass
public static void beforeClass() {
System.setProperty("some.property", "<value>");
}
// Optionally:
@AfterClass
public static void afterClass() {
System.clearProperty("some.property");
}
답변
구성에 PropertyPlaceholderConfigurer를 추가하는 것이 효과가 있습니다.
@Configuration
@ComponentScan
@EnableJpaRepositories
@EnableTransactionManagement
public class TestConfiguration {
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
builder.setType(EmbeddedDatabaseType.DERBY);
return builder.build();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPackagesToScan(new String[] { "com.test.model" });
// Use hibernate
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
entityManagerFactoryBean.setJpaProperties(getHibernateProperties());
return entityManagerFactoryBean;
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.show_sql", "false");
properties.put("hibernate.dialect", "org.hibernate.dialect.DerbyDialect");
properties.put("hibernate.hbm2ddl.auto", "update");
return properties;
}
@Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
entityManagerFactory().getObject()
);
return transactionManager;
}
@Bean
PropertyPlaceholderConfigurer propConfig() {
PropertyPlaceholderConfigurer placeholderConfigurer = new PropertyPlaceholderConfigurer();
placeholderConfigurer.setLocation(new ClassPathResource("application_test.properties"));
return placeholderConfigurer;
}
}
그리고 시험 수업에서
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
public class DataServiceTest {
@Autowired
private DataService dataService;
@Autowired
private DataRepository dataRepository;
@Value("${Api.url}")
private String baseUrl;
@Test
public void testUpdateData() {
List<Data> datas = (List<Data>) dataRepository.findAll();
assertTrue(datas.isEmpty());
dataService.updateDatas();
datas = (List<Data>) dataRepository.findAll();
assertFalse(datas.isEmpty());
}
}