如何使用Mockito在Spring中模拟自动装配的@Value字段?

Dav*_*ave 100 spring mockito autowired value-initialization

我正在使用Spring 3.1.4.RELEASE和Mockito 1.9.5.在我的Spring课程中,我有:

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

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

// ...
Run Code Online (Sandbox Code Playgroud)

从我目前设置的JUnit测试开始,如下所示:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class MyTest 
{ 
Run Code Online (Sandbox Code Playgroud)

我想为我的"defaultUrl"字段模拟一个值.请注意,我不想模拟其他字段的值 - 我想保留它们的原样,只保留"defaultUrl"字段.另请注意,setDefaultUrl我的课程中没有明确的"setter"方法(例如),我不想仅仅为了测试而创建任何方法.

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

geo*_*and 114

您可以使用Spring的魔力,ReflectionTestUtils.setField以避免对您的代码进行任何修改.

查看本教程以获取更多信息,尽管您可能不需要它,因为该方法非常易于使用

UPDATE

自Spring 4.2.RC1引入以来,现在可以设置静态字段而无需提供类的实例.请参阅文档和此提交的这一部分.

  • 万一链接失效,请执行以下操作:在测试期间调用bean方法之前,请使用ReflectionionTestUtils.setField(bean,“ fieldName”,“ value”);`。 (7认同)
  • 用于模拟从属性文件中检索的属性的好解决方案。 (3认同)

BAE*_*RUS 83

这是我第三次用Google搜索这篇文章,因为我总是忘记如何模拟一个@Value字段.虽然接受的答案是正确的,但我总是需要一些时间让"setField"调用正确,所以至少对我自己来说,我在这里粘贴一个示例代码段:

生产类:

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

测试类:

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")
Run Code Online (Sandbox Code Playgroud)


Man*_*nes 30

您还可以将属性配置模拟到测试类中

@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);
   }
}
Run Code Online (Sandbox Code Playgroud)


Men*_*ini 26

我使用了下面的代码,它对我有用:

@InjectMocks
private ClassABC classABC;

@Before
public void setUp() {
    ReflectionTestUtils.setField(classABC, "constantFromConfigFile", 3);
}
Run Code Online (Sandbox Code Playgroud)

参考:https : //www.jeejava.com/mock-an-autowired-value-field-in-spring-with-junit-mockito/


Mar*_*ark 17

我想建议一个相关的解决方案,即将带@Value注释的字段作为参数传递给构造函数,而不是使用ReflectionTestUtils该类.

而不是这个:

public class Foo {

    @Value("${foo}")
    private String foo;
}
Run Code Online (Sandbox Code Playgroud)

和

public class FooTest {

    @InjectMocks
    private Foo foo;

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

    @Test
    public void testFoo() {
        // stuff
    }
}
Run Code Online (Sandbox Code Playgroud)

做这个:

public class Foo {

    private String foo;

    public Foo(@Value("${foo}") String foo) {
        this.foo = foo;
    }
}
Run Code Online (Sandbox Code Playgroud)

和

public class FooTest {

    private Foo foo;

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

    @Test
    public void testFoo() {
        // stuff
    }
}
Run Code Online (Sandbox Code Playgroud)

这种方法的好处:1)我们可以在没有依赖容器的情况下实例化Foo类(它只是一个构造函数),2)我们没有将我们的测试耦合到我们的实现细节(反射使用字符串将我们绑定到字段名称,如果我们更改字段名称可能会导致问题).

  • 缺点:如果有人弄乱了注释,例如使用属性“ bar”而不是“ foo”,则测试仍然可以进行。我只有这种情况。 (2认同)

Thi*_*ult 13

你可以使用这个神奇的Spring Test注释:

@TestPropertySource(properties = { "my.spring.property=20" }) 
Run Code Online (Sandbox Code Playgroud)

请参阅 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() {
   ...
Run Code Online (Sandbox Code Playgroud)

这是属性的类:

@Component
public class MyClass {

  @Value("${my.spring.property}")
  private int mySpringProperty;
   ...
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案。供我自己参考的一个注释:您需要模拟您正在使用的所有@Values,您不能模拟第一个@Values,然后从属性中注入第二个@Values。 (3认同)
  • 对于服务层的单元测试,我们通常将“@ExtendWith(MockitoExtension.class)”与 JUnit5 结合使用。在这些情况下,与ReflectionTestUtils.setField(bean, "fieldName", "value")`解决方案相比,建议的方法可能更复杂,因为我们需要手动注入所有模拟而不是使用@InjectMocks。更简单的解决方案是在被测试服务的构造函数中自动加载依赖项和值,并手动实例化被测服务 - `Service serviceUnderTest = new ServiceImpl(Integer, String, RepositoryMock, OtherServiceMock)` (3认同)

Dhe*_*rik 5

另请注意,我的类中没有明确的“setter”方法(例如 setDefaultUrl),并且我不想仅出于测试目的而创建任何方法。

解决此问题的一种方法是将您的类更改为使用Constructor Injection,它可用于测试和 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;
    }

}
Run Code Online (Sandbox Code Playgroud)

在您的测试中,只需使用它:

MySpringClass MySpringClass  = new MySpringClass("anyUrl", "anyPassword");
Run Code Online (Sandbox Code Playgroud)