【问题标题】:Mockito in spring boot does not mock objectSpring Boot 中的 Mockito 不会模拟对象
【发布时间】:2019-11-19 08:12:42
【问题描述】:

我有一个简单的测试用例,我在 Spring Boot 应用程序中使用 Mockito。

userOperation.java

...
@Autowired
Validator nameValidator;

private boolean userOperation(String name) {
    NameStats nameStats = nameValidator.validate(name);
    if (!nameStats.isSuccess()) {
        throw new Exception();
    }
    return true;
}
...

Validator.java

public NameStats validate(String name)
{
    NameStats nameStats = new NameStats();
    // code goes here
    return nameStats;
}

testCase.java

...
@SpringBootTest
@RunWith(SpringRunner.class)
public class ImportFileOperationTest {

    @Mock
    Validator nameValidator;

    @Mock
    NameStats nameStats;

    @InjectMocks
    UserOperation operation;

    @Test
    public void  ConnectorDelegate0Test() {
        Mockito.when(nameValidator.validate(any(String.class))).thenReturn(nameStats);
        Mockito.when(nameStats.isSuccess()).thenReturn(true);
        operation.userOperation(restConsumerRequest);
    }

}
...

错误:

对于上面的测试用例,我得到了 NullPointerException。 在跟踪时,我发现 test 中的以下代码行返回 null 值。

Mockito.when(nameValidator.validate(any(String.class))).thenReturn(nameStats);

由于 null 值,当它尝试调用 nameStats.isSuccess() 时,它会抛出 nullpointexception,即使我已将此函数的响应模拟为 true

问题: 1. 我是否以正确的方式嘲笑回应? 2. mocking函数为什么返回null对象?

更新:

testCase.java

@SpringBootTest
@RunWith(SpringRunner.class)
public class ImportFileOperationTest {

    @MockBean
    Validator nameValidator;

    NameStats nameStats;

    @InjectMocks
    @Resource
    UserOperation operation;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void  ConnectorDelegate0Test() {
        nameStats = new nameStats();
        Mockito.when(nameValidator.validate(any(String.class))).thenReturn(nameStats);
        Mockito.when(nameStats.isSuccess()).thenReturn(true);
        operation.userOperation(restConsumerRequest);
    }

}

我尝试手动创建对象nameStats,并希望validate 函数返回该对象,但它仍然返回null

任何帮助将不胜感激。

【问题讨论】:

  • 您可以尝试在您的设置中注释掉 MockitoAnnotations 调用吗?您根本不需要做任何事情,Spring 会完成这项工作。
  • 另外,既然你@InjectMocks,就没有(明显的)需要@MockBean。你能试着用@Mock替换@MockBean吗?
  • @MockBean 需要使用。 @InjectMocks 不起作用 - 您需要正确配置 Spring 自动装配。
  • 1.使用构造函数注入。停止自动装配字段,您不需要这样做。 2.如果不需要,不要使用spring来测试你的类。 3. 手动初始化类并将你的模拟提供给 id。注入模拟不是 IoC/DI 框架,并且有很大的陷阱。如果你这样做,你可以用一种简单易懂的方式测试你的 java 类。如果你确实需要模拟一个 spring bean,请阅读 `@MockBean'
  • 如果你想模拟,你应该使用RunWith(MockitoRunner.class),而不是SpringRunner

标签: java unit-testing spring-boot mockito


【解决方案1】:

测试应该是 Mockito 驱动或 Spring 驱动的。 Spring 驱动将具有 @SpringBootTest@RunWith(SpringRunner.class) 类级别注释,并且模拟将使用 @MockBean 声明或使用 Mockito.initMocks 显式实例化。 Mockito 驱动的测试将具有@RunWith(MockitoJUnitRunner.class),并且模拟将使用@Mock 声明和/或注入@InjectMocks。请注意,当 NameStats 等实例在方法中使用 new 实例化时,注入没有帮助。

显示的测试不必是@SpringBootTest,因此它可以写成这样的 Mockito 驱动测试:

@RunWith(MockitoJUnitRunner.class)
public class ImportFileOperationTest {

@Mock
Validator nameValidator;

@InjectMocks
UserOperation operation;

@Test
public void  connectorDelegateTest() {
    NameStats nameStats = new NameStats();
    nameStats.setStats(Stats.SUCCESS);
    Mockito.when(nameValidator.validate(any(String.class))).thenReturn(nameStats);

    operation.userOperation(restConsumerRequest);
}

【讨论】:

    【解决方案2】:

    您需要将@InjectMocks 用于所有类类型,@Mock 需要用于该类内的所有关联属性。

    例子:

     class Product {
    
     ProductDetails  productDetails;
    
     }
    
     class ProductDetails {
    
     }
    

    上述类的示例测试用例:

    @SpringBootTest
    @RunWith(SpringRunner.class)
    public class ProductTest {
    
        @Mock
        ProductDetails productDetails;
    
        @InjectMocks
        Product product;
    
    }
    

    【讨论】:

    • 我终于明白在哪里使用 InjectMocks 和 Mock 注解了。只是一个建议,在类的开头添加/编辑 InjectMocks 代码,然后添加 @Mock 代码。
    • 你能详细说明一下吗?
    【解决方案3】:

    升级到 Spring Boot 2 后遇到类似问题。 原因在这里解释https://javadoc.io/static/org.mockito/mockito-core/3.3.3/org/mockito/ArgumentMatchers.html#any-java.lang.Class-

    自 Mockito 2.1.0 起,只允许 的非 null 实例,因此 null 不再是有效值。

    所以 mock 返回 null 或空列表,因为调用是用 anyString() 设置的,并且传递的对象是 null。要匹配 value 或 null,请使用 matcher any()

    【讨论】:

    • 为我节省了很多时间。谢谢!
    猜你喜欢
    • 2015-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-28
    • 1970-01-01
    • 2020-02-17
    • 1970-01-01
    相关资源
    最近更新 更多