Mockito로 Spring에서 autowired @Value 필드를 어떻게 모의합니까?


124

Spring 3.1.4.RELEASE 및 Mockito 1.9.5를 사용하고 있습니다. 내 Spring 수업에는 다음이 있습니다.

@Value("#{myProps['default.url']}")
private String defaultUrl;

@Value("#{myProps['default.password']}")
private String defaultrPassword;

// ...

내 JUnit 테스트에서 현재 다음과 같이 설정했습니다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest 
{ 

내 "defaultUrl"필드의 값을 모의하고 싶습니다. 다른 필드에 대한 값을 모의하지 않고 "defaultUrl"필드 만 그대로 유지하고 싶습니다. 또한 setDefaultUrl클래스에 명시적인 "setter"메소드 (예 :)가 없으며 테스트 목적으로 만 생성하고 싶지 않습니다.

이를 감안할 때 해당 필드의 값을 어떻게 모의 할 수 있습니까?

답변:


145

ReflectionTestUtils.setField코드를 수정하는 것을 피하기 위해 Spring의 마법을 사용할 수 있습니다 .

방법은 사용하기 매우 쉽기 때문에 필요하지 않을 수 있지만 더 자세한 정보는 튜토리얼을 확인하십시오.

최신 정보

Spring 4.2.RC1이 도입 된 이후로 클래스의 인스턴스를 제공하지 않고도 정적 필드를 설정할 수 있습니다. 문서 의이 부분 과이 커밋을 참조하십시오 .


12
링크가 죽은 경우 : 테스트 중에 메서드 ReflectionTestUtils.setField(bean, "fieldName", "value");를 호출하기 전에 사용 하십시오 bean.
Michał Stochmal

2
속성 파일에서 검색하는 속성을 조롱하기위한 좋은 솔루션입니다.
Antony Sampath Kumar Reddy

@ MichałStochmal, filed가 private java.lang.IllegalStateException이므로 생성 할 것입니다. 메소드에 액세스 할 수 없습니다. org.springframework.util.ReflectionUtils 클래스는 수정자를 사용하여 com.kaleidofin.app.service.impl.CVLKRAProvider 클래스의 멤버에 액세스 할 수 없습니다. ""at org.springframework.util.ReflectionUtils.handleReflectionException (ReflectionUtils.java:112) at org.springframework.util.ReflectionUtils.setField (ReflectionUtils.java:655)
Akhil Surapuram

112

@Value 필드를 조롱하는 방법을 항상 잊어 버렸기 때문에 이제 세 번째 로이 SO 게시물을 검색했습니다. 받아 들여진 대답은 맞지만 "setField"호출을 제대로 받기 위해서는 항상 시간이 필요하므로 적어도 나 자신을 위해 여기에 예제 스 니펫을 붙여 넣습니다.

생산 클래스 :

@Value("#{myProps[‘some.default.url']}")
private String defaultUrl;

테스트 클래스 :

import org.springframework.test.util.ReflectionTestUtils;

ReflectionTestUtils.setField(instanceUnderTest, "defaultUrl", "http://foo");
// Note: Don't use MyClassUnderTest.class, use the instance you are testing itself
// Note: Don't use the referenced string "#{myProps[‘some.default.url']}", 
//       but simply the FIELDs name ("defaultUrl")

32

속성 구성을 테스트 클래스로 모의 할 수도 있습니다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest 
{ 
   @Configuration
   public static class MockConfig{
       @Bean
       public Properties myProps(){
             Properties properties = new Properties();
             properties.setProperty("default.url", "myUrl");
             properties.setProperty("property.value2", "value2");
             return properties;
        }
   }
   @Value("#{myProps['default.url']}")
   private String defaultUrl;

   @Test
   public void testValue(){
       Assert.assertEquals("myUrl", defaultUrl);
   }
}

25

클래스 @Value를 사용하는 대신 -annotated 필드를 생성자에 매개 변수로 전달하는 관련 솔루션을 제안하고 싶습니다 ReflectionTestUtils.

대신 :

public class Foo {

    @Value("${foo}")
    private String foo;
}

public class FooTest {

    @InjectMocks
    private Foo foo;

    @Before
    public void setUp() {
        ReflectionTestUtils.setField(Foo.class, "foo", "foo");
    }

    @Test
    public void testFoo() {
        // stuff
    }
}

이 작업을 수행:

public class Foo {

    private String foo;

    public Foo(@Value("${foo}") String foo) {
        this.foo = foo;
    }
}

public class FooTest {

    private Foo foo;

    @Before
    public void setUp() {
        foo = new Foo("foo");
    }

    @Test
    public void testFoo() {
        // stuff
    }
}

이 접근 방식의 이점 : 1) 종속성 컨테이너없이 Foo 클래스를 인스턴스화 할 수 있습니다 (단지 생성자). 2) 테스트를 구현 세부 사항에 연결하지 않습니다 (반영은 문자열을 사용하여 필드 이름에 연결하고 필드 이름을 변경하면 문제가 발생할 수 있습니다.)


3
단점 : 누군가가 주석을 엉망으로 만드는 경우, 예를 들어 'foo'대신 'bar'속성을 사용하면 테스트가 계속 작동합니다. 이 사건이 있습니다.
닐스 엘 Himoud

@ NilsEl-Himoud 그것은 일반적으로 OP 질문에 대한 공정한 요점이지만 귀하가 제기하는 문제는 리플렉션 유틸리티 대 생성자를 사용하는 것이 더 좋거나 나쁘지 않습니다. 이 답변의 요점은 리플렉션 util (허용되는 답변)보다 생성자에 대한 고려였습니다. Mark, 대답 해 주셔서 감사합니다.이 조정의 용이성과 청결함에 감사드립니다.
Marquee

22

이 마법의 Spring Test 주석을 사용할 수 있습니다.

@TestPropertySource(properties = { "my.spring.property=20" }) 

org.springframework.test.context.TestPropertySource 참조

예를 들어 다음은 테스트 클래스입니다.

@ContextConfiguration(classes = { MyTestClass.Config.class })
@TestPropertySource(properties = { "my.spring.property=20" })
public class MyTestClass {

  public static class Config {
    @Bean
    MyClass getMyClass() {
      return new MyClass ();
    }
  }

  @Resource
  private MyClass myClass ;

  @Test
  public void myTest() {
   ...

그리고 이것은 속성이있는 클래스입니다.

@Component
public class MyClass {

  @Value("${my.spring.property}")
  private int mySpringProperty;
   ...


1

또한 내 클래스에는 명시적인 "setter"메서드 (예 : setDefaultUrl)가 없으며 테스트 목적으로 만 생성하고 싶지 않습니다.

이를 해결하는 한 가지 방법은 생성자 주입 을 사용하도록 클래스를 변경하는 것입니다. 은 테스트 및 Spring 주입에 사용되는 입니다. 더 이상 반사하지 마세요 :)

따라서 생성자를 사용하여 모든 문자열을 전달할 수 있습니다.

class MySpringClass {

    private final String defaultUrl;
    private final String defaultrPassword;

    public MySpringClass (
         @Value("#{myProps['default.url']}") String defaultUrl, 
         @Value("#{myProps['default.password']}") String defaultrPassword) {
        this.defaultUrl = defaultUrl;
        this.defaultrPassword= defaultrPassword;
    }

}

그리고 테스트에서 사용하십시오.

MySpringClass MySpringClass  = new MySpringClass("anyUrl", "anyPassword");
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.