【问题标题】:How to mock a local variable that's being set in an external method call using Mockito如何使用 Mockito 模拟在外部方法调用中设置的局部变量
【发布时间】:2021-09-24 13:58:58
【问题描述】:

我有一个类似的代码。

@Component
public class Example {
  
  @Autowired
  private GreetingService greetService;
    
  public String greet(String name) {
    AtomicReference<SomeCustomClass> temp = new AtomicReference<>(); 
    String greeting = greetService.getGreeting(temp);  // The method sets a value for temp
    if(!Objects.isNull(temp.get())) {return greeting + " " + name;}
    return "Hello, user";
  }

}

我知道这是一个经过深思熟虑的例子,但我有一个类似的用例,它是这个的复杂版本。我想测试这个我想测试if 块的类。谁能帮我理解一下,如何测试if块内的代码。

【问题讨论】:

  • 这里有太多问题了,从字段注入与构造函数注入到出于任何原因使用AtomicReference
  • 注入一个GreetingService,将temp设置为非空值。
  • GreetingService 将是一个模拟对象,对吧? @AndyTurner 如何设置局部变量的值?你能详细说明一下吗?
  • @Eugene 我只是在这里使用了场注入来更好地理解。 :) 我在实际代码中使用了构造注入。该项目实际上是一个反应流项目。我在哪里调用使用 WebClient 调用外部服务的方法。用例是这样的,一旦我得到 WebClient 的响应,我需要验证它。如果验证失败,我仍然需要以某种方式返回响应。所以,这就是对 AtomicReference 的引用正在做的事情。很抱歉这个模糊的例子

标签: java spring junit mockito junit5


【解决方案1】:

下面是我如何在不触碰代码的情况下测试它。我确实想指出,这个问题中的代码是以促进测试实现细节而不是类接口的方式编写的。这种测试不支持重构,因为它们会中断。

我对这个编码使用的类型做了一些假设:

@Component
public class GreetingService {

    public String getGreeting(AtomicReference<SomeCustomClass> someCustomClass) {
        return someCustomClass.get().getSomeValue();
    }
}
public class SomeCustomClass {

    private String someValue;

    public String getSomeValue() {
        return someValue;
    }

    public void setSomeValue(String s) {
        this.someValue = s;
    }
}

这里的计划是模拟GreetingService.getGreeting,让我们可以在测试期间操纵temp。 代码使用了私有字段注入,这意味着我们要启动 Spring 的所有花里胡哨,这是通过使用 @SpringBootTest 注释来实现的。

当 Spring 创建我们的 Example 组件时,我们希望它使用我们的模拟 GreetingService,这可以通过在测试中声明 @MockBean 字段来实现。

现在在测试中,我们要操作temp,它从Example.greet 内部传递到GreetingService,为此我们可以使用Mockitos then(Answer&lt;T&gt;) 方法。在Answer 中,我们可以操作temp 引用。这是最终结果:

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import java.util.concurrent.atomic.AtomicReference;

import org.junit.jupiter.api.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

@SpringBootTest
public class ExampleTest {

    @Autowired
    private Example example;

    @MockBean
    private GreetingService greetingService;

    @Test
    public void returnsGreetingByNameIfValueIsNotNull() {
        // arrange
        when(greetingService.getGreeting(any())).then(new Answer<String>() {
            @Override
            public String answer(InvocationOnMock invocation) throws Throwable {
                SomeCustomClass testSomeCustomClass = new SomeCustomClass();
                testSomeCustomClass.setSomeValue("Aloha");

                AtomicReference<SomeCustomClass> ref = invocation.getArgument(0, AtomicReference.class);
                ref.set(testSomeCustomClass);
                return ref.get().getSomeValue();
            }
        });

        // act
        String result = example.greet("Jon Doe");

        // assert
        assertThat(result).isEqualTo("Aloha Jon Doe");
    }

    @Test
    public void returnsDefaultGreetingIfValueIsNull() {
        // arrange
        when(greetingService.getGreeting(any())).then(new Answer<String>() {
            @Override
            public String answer(InvocationOnMock invocation) throws Throwable {
                return null;
            }
        });

        // act
        String result = example.greet("Jon Doe");

        // assert
        assertThat(result).isEqualTo("Hello, user");
    }
}

【讨论】:

    猜你喜欢
    • 2014-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-19
    • 1970-01-01
    相关资源
    最近更新 更多