programing

Mockito와 함께 봄의 자동 전원 @Value 필드를 조롱하려면 어떻게 해야 하나요?

testmans 2023. 3. 16. 21:14
반응형

Mockito와 함께 봄의 자동 전원 @Value 필드를 조롱하려면 어떻게 해야 하나요?

Spring 3.1.4를 사용하고 있습니다.릴리즈 및 모키토 1.9.5.봄 수업에는 다음과 같은 것이 있습니다.

@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"로 지정합니다. 명시적인 예: "setter" 메서드)는.setDefaultUrl테스트 목적으로만 만들고 싶지는 않습니다.

이 경우 해당 필드의 값을 어떻게 조롱할 수 있습니까?

할 수 .ReflectionTestUtils.setField코드를 변경하지 않도록 합니다.

Michau Stochmal의 코멘트는 다음과 같은 예를 제시합니다.

ReflectionTestUtils.setField(bean, "fieldName", "value");bean방법을 지정합니다.

이 방법은 매우 사용하기 쉽기 때문에 필요 없을 수도 있지만 자세한 내용은 이 튜토리얼을 참조하십시오.

갱신하다

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

@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")

다음 매직 스프링 테스트 주석을 사용할 수 있습니다.

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

org.springframework.test.disc를 참조하십시오.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;
   ...

저는 이와 된 해결책을. 즉, ''을 하는 것입니다.@Value로서 constructor를 constructor에 대한 parameter로서 constructor 합니다.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) 구현 세부사항에 테스트를 결합하지 않습니다(반영에 의해 필드 이름이 연결되므로 필드 이름이 변경될 경우 문제가 발생할 수 있습니다).

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

@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);
   }
}

아래 코드를 사용했는데, 작동했습니다.

@InjectMocks
private ClassABC classABC;

@Before
public void setUp() {
    ReflectionTestUtils.setField(classABC, "constantFromConfigFile", 3);
}

참고 자료: https://www.jeejava.com/mock-an-autowired-value-field-in-spring-with-junit-mockito/

또, 클래스에는 명시적인 「setter」메서드(setDefaultUrl 등)가 없기 때문에, 테스트의 목적으로만 작성하는 것은 원하지 않습니다.

이 문제를 해결하는 한 가지 방법은 클래스를 테스트 및 스프링 주입에 사용할 수 있는 생성자 주입을 사용하도록 변경하는 것입니다.반사는 없습니다:)

따라서 컨스트럭터를 사용하여 임의의 문자열을 전달할 수 있습니다.

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");

가능하면 테스트 클래스에서 액세스할 수 있도록 필드 가시성을 패키지 보호로 설정합니다.나는 Guava의 @VisibleForTesting 주석을 사용하여 기록한다(다음 사람이 왜 비공개로 되어 있지 않은지 궁금할 경우).이렇게 하면 필드의 문자열 이름에 의존할 필요가 없고 모든 것이 안전하게 유지됩니다.

학교에서 배운 표준 캡슐화 관행에 어긋난다는 것을 알고 있습니다.하지만 팀 내에서 이런 방향으로 가는 것에 대한 합의가 이루어지자마자, 저는 그것이 가장 실용적인 해결책이라는 것을 알게 되었습니다.

또 다른 방법은@SpringBootTest주석properties들판.

여기서 덮어씁니다.example.firstProperty속성:

@SpringBootTest(properties = { "example.firstProperty=annotation" })
public class SpringBootPropertySourceResolverIntegrationTest {

    @Autowired private PropertySourceResolver propertySourceResolver;

    @Test
    public void shouldSpringBootTestAnnotation_overridePropertyValues() {
        String firstProperty = propertySourceResolver.getFirstProperty();
        String secondProperty = propertySourceResolver.getSecondProperty();

        Assert.assertEquals("annotation", firstProperty);
        Assert.assertEquals("defaultSecond", secondProperty);
    }
}

보시다시피 하나의 속성만 덮어씁니다.에 기재되어 있지 않은 속성@SpringBootTest손대지 않다따라서 테스트를 위해 특정 속성만 덮어쓸 필요가 있는 경우 이는 훌륭한 솔루션입니다.

단일 속성의 경우 대괄호 없이 쓸 수 있습니다.

@SpringBootTest(properties = "example.firstProperty=annotation")

답변: https://www.baeldung.com/spring-tests-override-properties#springBootTest

또한 Dherik answer(https://stackoverflow.com/a/52955459/1673775)에서와 같이 가능한 한 속성을 파라미터로 전달하여 유닛 테스트에서 쉽게 속성을 모킹할 수 있도록 하는 것이 좋습니다.

그러나 통합 테스트에서는 개체를 수동으로 생성하지 않는 경우가 많지만 다음과 같습니다.

  • 사용하다@Autowired
  • 통합 테스트에서 사용되는 클래스에서 사용되는 속성은 직접 사용되는 클래스의 종속성이 크기 때문에 간접적으로 수정하려고 합니다.

그리고 이 해결방법은@SpringBootTest도움이 될 것 같아요

언급URL : https://stackoverflow.com/questions/23162777/how-do-i-mock-an-autowired-value-field-in-spring-with-mockito

반응형