spring - 在 Spring 环境中仅模拟选定的属性

标签 spring unit-testing spring-boot junit mockito

我希望能够使用测试属性文件并且只覆盖一些属性。必须覆盖每个属性会很快变得丑陋。

这是我用来测试我在测试用例中模拟属性和使用现有属性的能力的代码

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyApp.class)
@TestPropertySource(
      locations = { "classpath:myapp-test.properties" },
      properties = { "test.key = testValue" })
public class EnvironmentMockedPropertiesTest {

   @Autowired private Environment env;
   // @MockBean private Environment env;

   @Test public void testExistingProperty() {
      // some.property=someValue
      final String keyActual = "some.property";
      final String expected = "someValue";
      final String actual = env.getProperty(keyActual);
      assertEquals(expected, actual);
   }

   @Test public void testMockedProperty() {
      final String keyMocked = "mocked.test.key";
      final String expected = "mockedTestValue";
      when(env.getProperty(keyMocked)).thenReturn(expected);
      final String actual = env.getProperty(keyMocked);
      assertEquals(expected, actual);
   }

   @Test public void testOverriddenProperty() {
      final String expected = "testValue";
      final String actual = env.getProperty("test.key");
      assertEquals(expected, actual);
   }

}

我发现的是:

  • @Autowired private Environment env;
    • testExistingProperty()testOverriddenProperty() 通过
    • testMockedProperty() 失败
  • @MockBean private Environment env;
    • testMockedProperty() 通过
    • testExistingProperty()testOverriddenProperty() 失败

有没有办法实现我的目标?

依赖关系:

<spring.boot.version>1.4.3.RELEASE</spring.boot.version>
...
<!-- Spring -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot</artifactId>
   <version>${spring.boot.version}</version>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-autoconfigure</artifactId>
   <version>${spring.boot.version}</version>
</dependency>
<!-- Starter for testing Spring Boot applications with libraries including JUnit,
   Hamcrest and Mockito -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <version>${spring.boot.version}</version>
</dependency>

最佳答案

好的,我已经完成了这项工作,您需要使用 Mockito 来完成您正在寻找的内容:

Maven 依赖

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.6.4</version>
</dependency>

测试类设置

import static org.mockito.Mockito.*;
import static org.springframework.test.util.AopTestUtils.getTargetObject;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyApp.class)
@TestPropertySource(
      locations = { "classpath:myapp-test.properties" },
      properties = { "test.key = testValue" })

public class AnswerTest {

    // This will be only for injecting, we will not be using this object in tests.
    @Autowired
    private Environment env;

    // This is the reference that will be used in tests.
    private Environment envSpied;

    // Map of properties that you intend to mock
    private Map<String, String> mockedProperties;

    @PostConstruct
    public void postConstruct(){
        mockedProperties = new HashMap<String, String>();
        mockedProperties.put("mocked.test.key_1", "mocked.test.value_1");
        mockedProperties.put("mocked.test.key_2", "mocked.test.value_2");
        mockedProperties.put("mocked.test.key_3", "mocked.test.value_3");

        // We use the Spy feature of mockito which enabled partial mocking
        envSpied = Mockito.spy((Environment) getTargetObject(env));

        // We mock certain retrieval of certain properties
        // based on the logic contained in the implementation of Answer class
        doAnswer(new CustomAnswer()).when(envSpied).getProperty(Mockito.anyString());
    }

测试用例

    // Testing for both mocked and real properties in same test method
    @Test public void shouldReturnAdequateProperty() {
        String mockedValue = envSpied.getProperty("mocked.test.key_3");
        String realValue = envSpied.getProperty("test.key");

        assertEquals(mockedValue, "mocked.test.value_3");
        assertEquals(realValue, "testValue");
    }

Mockito的Answer接口(interface)实现

    // Here we define what should mockito do:
    // a) return mocked property if the key is a mock
    // b) invoke real method on Environment otherwise
    private class CustomAnswer implements Answer<String>{

        @Override
        public String answer(InvocationOnMock invocationOnMock) throws Throwable {
            Object[] arguments = invocationOnMock.getArguments();
            String parameterKey = (String) arguments[0];

            String mockedValue = mockedProperties.get(parameterKey);

            if(mockedValue != null){
                return mockedValue;
            }

            return (String) invocationOnMock.callRealMethod();
        }
    }
}

尝试一下,如果一切都清楚,请告诉我。

关于spring - 在 Spring 环境中仅模拟选定的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41775394/

相关文章:

java - 以 thymeleaf 形式传播对象

asp.net-mvc - asp.net MVC 中的单元测试,如何模拟页面请求?

spring-mvc - Spring-boot 从 Controller 返回 json 和 xml

java - 构造函数需要一个无法找到的 bean 类型

json - 将 jdbctemplate 结果转换为 json 的最有效方法?

jquery - 所需的字符串参数 'userName' 不存在

spring - 用tomcat和spring模拟http 503状态

java - 从命令行运行 java 类,包括 jar 中的库

c# - 是否有支持任意表达式而不是一组有限的临时方法的 C# 单元测试框架?

java - 在 Heroku 上使用 Spring Boot 的 Postgresql 问题