【问题标题】:Mock only selected properties in Spring Environment在 Spring Environment 中仅模拟选定的属性
【发布时间】:2017-06-06 03:15:17
【问题描述】:

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

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

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

【问题讨论】:

  • 我假设你想通过只使用一个环境 env 变量来实现这一点,它既可以处理模拟数据,也可以处理真实数据,对吧?

标签: spring unit-testing spring-boot junit mockito


【解决方案1】:

好的,我已经完成了这项工作,您需要使用 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 接口的实现

    // 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();
        }
    }
}

试试看,如果一切都清楚了,请告诉我。

【讨论】:

  • getTargetObject() 来自哪里?
  • 抱歉忘记进口了。我已经更新了帖子
  • 嗯,它要复杂得多,但它确实有效,非常感谢。 :) 我猜@MockBean 根本没有做我想做的事,这很遗憾:我认为嘲笑的想法是你只覆盖你真正需要的东西(或者那是间谍?)。
  • 这是两个不同的概念。你需要一个间谍,但最重要的是你需要基于参数的条件解析,而我在我的职业生涯中只遇到过几次。
猜你喜欢
  • 2011-02-02
  • 1970-01-01
  • 2019-09-06
  • 2011-09-17
  • 2022-01-24
  • 2019-03-29
  • 2020-02-16
  • 1970-01-01
  • 2013-05-27
相关资源
最近更新 更多