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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23162777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 06:40:20  来源:igfitidea点击:

How do I mock an autowired @Value field in Spring with Mockito?

springmockitoautowiredvalue-initialization

提问by Dave

I'm using Spring 3.1.4.RELEASE and Mockito 1.9.5. In my Spring class I have:

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

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

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

// ...

From my JUnit test, which I currently have set up like so:

从我的 JUnit 测试中,我目前已设置如下:

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

I would like to mock a value for my "defaultUrl" field. Note that I don't want to mock values for the other fields — I'd like to keep those as they are, only the "defaultUrl" field. Also note that I have no explicit "setter" methods (e.g. setDefaultUrl) in my class and I don't want to create any just for the purposes of testing.

我想为我的“defaultUrl”字段模拟一个值。请注意,我不想模拟其他字段的值——我想保持它们的原样,只有“defaultUrl”字段。另请注意,我的类中没有明确的“setter”方法(例如setDefaultUrl),并且我不想仅出于测试目的而创建任何方法。

Given this, how can I mock a value for that one field?

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

回答by geoand

You can use the magic of Spring's ReflectionTestUtils.setFieldin order to avoid making any modifications whatsoever to your code.

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

Check out thistutorial for even more information, although you probably won't need it since the method is very easy to use

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

UPDATE

更新

Since the introduction of Spring 4.2.RC1 it is now possible to set a static field without having to supply an instance of the class. See thispart of the documentation and thiscommit.

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

回答by BAERUS

It was now the third time I googled myself to this SO post as I always forget how to mock an @Value field. Though the accepted answer is correct, I always need some time to get the "setField" call right, so at least for myself I paste an example snippet here:

现在是我第三次用谷歌搜索这个 SO 帖子,因为我总是忘记如何模拟 @Value 字段。虽然接受的答案是正确的,但我总是需要一些时间来正确调用“setField”,所以至少对我自己来说,我在这里粘贴了一个示例片段:

Production class:

制作类:

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

Test class:

测试类:

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

回答by Manuel Quinones

You can also mock your property configuration into your test class

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

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

回答by Mark

I'd like to suggest a related solution, which is to pass the @Value-annotated fields as parameters to the constructor, instead of using the ReflectionTestUtilsclass.

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

Instead of this:

取而代之的是:

public class Foo {

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

and

public class FooTest {

    @InjectMocks
    private Foo foo;

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

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

Do this:

做这个:

public class Foo {

    private String foo;

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

and

public class FooTest {

    private Foo foo;

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

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

Benefits of this approach: 1) we can instantiate the Foo class without a dependency container (it's just a constructor), and 2) we're not coupling our test to our implementation details (reflection ties us to the field name using a string, which could cause a problem if we change the field name).

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

回答by Thibault

You can use this magic Spring Test annotation :

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

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

see org.springframework.test.context.TestPropertySource

org.springframework.test.context.TestPropertySource

For example, this is the test class :

例如,这是测试类:

@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() {
   ...

And this is the class with the property :

这是具有属性的类:

@Component
public class MyClass {

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

回答by Mendon Ashwini

I used the below code and it worked for me:

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

@InjectMocks
private ClassABC classABC;

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

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

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

回答by Dherik

Also note that I have no explicit "setter" methods (e.g. setDefaultUrl) in my class and I don't want to create any just for the purposes of testing.

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

One way to resolve this is change your class to use Constructor Injection, that is used for testing and Spring injection. No more reflection :)

解决此问题的一种方法是将您的类更改为使用Constructor Injection,用于测试和 Spring 注入。没有更多的反思:)

So, you can pass any String using the constructor:

因此,您可以使用构造函数传递任何字符串:

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

}

And in your test, just use it:

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

MySpringClass MySpringClass  = new MySpringClass("anyUrl", "anyPassword");