我试图为我的程序中用于验证表单的简单bean编写单元测试。该bean使用@Component进行注释,并且有一个初始化使用的类变量

@Value("${this.property.value}") private String thisProperty;

我想为这个类中的验证方法编写单元测试,但是,如果可能的话,我想这样做而不使用属性文件。我这样做的原因是,如果我从属性文件中提取的值发生了变化,我希望它不影响我的测试用例。我的测试用例是测试验证值的代码,而不是值本身。

是否有一种方法可以在我的测试类中使用Java代码来初始化一个Java类,并在该类中填充Spring @Value属性,然后使用它来测试?

我确实发现这个如何,似乎是接近,但仍然使用一个属性文件。我宁愿全部都是Java代码。


当前回答

从Spring 4.1开始,你可以在代码中通过在单元测试类级别上使用org.springframework.test.context.TestPropertySource注释来设置属性值。您甚至可以使用这种方法向依赖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();
    }

  }

}

注意:在Spring上下文中必须有org.springframework.context.support.PropertySourcesPlaceholderConfigurer实例

编辑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);
  }

}

其他回答

在springboot 2.4.1中,我刚刚在我的测试中添加了注释@SpringBootTest,显然,在我的src/test/resources/application.yml中设置了spring.profiles.active = test

我使用@ExtendWith({SpringExtension.class})和@ContextConfiguration(类= {RabbitMQ.class, GenericMapToObject.class, ModelMapper.class, StringUtils.class})进行外部conf

不要滥用反射获取/设置的私有字段

在这里的几个答案中使用反射是我们可以避免的。 它在这里带来的价值很小,但它呈现出多个缺点:

we detect reflection issues only at runtime (ex: fields not existing any longer) We want encapsulation but not a opaque class that hides dependencies that should be visible and make the class more opaque and less testable. it encourages bad design. Today you declare a @Value String field. Tomorrow you can declare 5 or 10 of them in that class and you may not even be straight aware that you decrease the design of the class. With a more visible approach to set these fields (such as constructor) , you will think twice before adding all these fields and you will probably encapsulate them into another class and use @ConfigurationProperties.

使你的类在单一和集成中都可测试

为了能够为spring组件类编写简单的单元测试(即没有运行的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;
    }

    //...         
}

单元测试示例

你可以在没有Spring的情况下实例化Foo,并通过构造函数为属性注入任何值:

public class FooTest{

   Foo foo = new Foo("dummyValue");

   @Test
   public void doThat(){
      ...
   }
}

集成测试示例

多亏了@SpringBootTest的properties属性,你可以用这种简单的方式在SpringBoot的上下文中注入属性:

@SpringBootTest(properties="property.value=dummyValue")
public class FooTest{
    
   @Autowired
   Foo foo;
     
   @Test
   public void doThat(){
       ...
   }    
}

您可以使用替代的@TestPropertySource,但它添加了一个额外的注释:

@SpringBootTest
@TestPropertySource(properties="property.value=dummyValue")
public class FooTest{ ...}

使用Spring(没有Spring Boot),它应该更复杂一点,但由于我很长时间没有使用Spring,所以我不喜欢说愚蠢的事情。

旁注:如果要设置许多@Value字段,将它们提取到带有@ConfigurationProperties注释的类中更相关,因为我们不希望构造函数带有太多参数。

如果可能的话,我会尝试在没有Spring Context的情况下编写这些测试。如果您在没有spring的测试中创建这个类,那么您可以完全控制它的字段。

要设置@value字段,您可以使用Springs ReflectionTestUtils -它有一个方法setField来设置私有字段。

@see JavaDoc: ReflectionTestUtils.setField(java.lang)Object, java . lang。管柱,java . lang . Object)

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

@InjectMocks
private ClassNotify classNotify;

@BeforeEach
  void init() {
    closeable = MockitoAnnotations.openMocks(this);
    ReflectionTestUtils.setField(classNotify, "EventType", "test-event");

  }

SpringBoot自动为我们做了很多事情,但当我们使用@SpringBootTest注释时,我们认为所有事情都将由SpringBoot自动解决。

有很多文档,但最少的是选择一个引擎(@RunWith(SpringRunner.class)),并指出将用于创建上下文以加载配置的类(resources/applicationl.properties)。

简单地说,你需要引擎和上下文:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyClassTest .class)
public class MyClassTest {

    @Value("${my.property}")
    private String myProperty;

    @Test
    public void checkMyProperty(){
        Assert.assertNotNull(my.property);
    }
}

当然,如果你查看Spring Boot文档,你会发现数千种操作系统的方法。