我使用的是Spring 3.1.4。RELEASE和Mockito 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),我不想仅仅为了测试的目的而创建任何方法。

鉴于此,我如何模拟一个值的一个字段?


当前回答

另一种方法是使用@SpringBootTest注释属性字段。

这里我们重写了示例。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

我还鼓励您尽可能在构造函数中传递property作为参数,如Dherik answer (https://stackoverflow.com/a/52955459/1673775),因为它使您能够在单元测试中轻松模拟属性。

然而,在集成测试中,你通常不会手动创建对象,但是:

你使用@Autowired 您希望修改集成测试中间接使用的类中使用的属性,因为它是一些直接使用的类的深层依赖项。

那么这个@SpringBootTest的解决方案可能会有帮助。

其他回答

只要可能,我将字段可见性设置为包保护的,以便可以从测试类访问它。我使用Guava的@VisibleForTesting注释(以防下一个人想知道为什么它不是私有的)来记录它。这样我就不必依赖于字段的字符串名称,并且一切都保持类型安全。

我知道这违背了我们在学校学到的标准封装实践。但一旦团队成员达成一致,我就会发现这是最务实的解决方案。

您还可以在测试类中模拟您的属性配置

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

您可以使用这个神奇的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;
   ...

还要注意,在我的类中没有显式的“setter”方法(例如setDefaultUrl),我不想仅仅为了测试的目的而创建任何方法。

解决这个问题的一种方法是将你的类改为使用构造函数注入,它可以用于测试和Spring注入。没有更多的思考:)

因此,你可以使用构造函数传递任何String:

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

我使用下面的代码,它为我工作:

@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/