Spring @MockBean 未注入 Cucumber

问题描述 投票:0回答:4

我正在实施一个

SchedulerService
,它使用
AgentRestClient
bean 从外部系统获取一些数据。它看起来像这样:

@Service
public class SchedulerService {

  @Inject
  private AgentRestClient agentRestClient;

  public String updateStatus(String uuid) {
    String status = agentRestClient.get(uuid);
    ...
  }
  ...
}

为了测试此服务,我使用 Cucumber 同时尝试使用 Spring Boot 的

AgentRestClient
注释来模拟
@MockBean
的行为,如下所示:

import cucumber.api.CucumberOptions;
import cucumber.api.java.Before;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = CentralApp.class)
@CucumberOptions(glue = {"com.company.project.cucumber.stepdefs", "cucumber.api.spring"})
public class RefreshActiveJobsStepDefs {

  @MockBean
  private AgentRestClient agentRestClient;

  @Inject
  private SchedulerService schedulerService;

  @Before
  public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
    given(agentRestClient.get(anyString())).willReturn("FINISHED");//agentRestClient is always null here
  }

  //Skipping the actual Given-When-Then Cucumber steps...
}

当我尝试运行任何 Cucumber 场景时,

agentRestClient
永远不会被模拟/注入。
setUp()
方法失败并出现 NPE:

java.lang.NullPointerException
  at com.company.project.cucumber.stepdefs.scheduler.RefreshActiveJobsStepDefs.setUp(RefreshActiveJobsStepDefs.java:38)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  at cucumber.runtime.Utils$1.call(Utils.java:37)
  at cucumber.runtime.Timeout.timeout(Timeout.java:13)
  at cucumber.runtime.Utils.invoke(Utils.java:31)
  at cucumber.runtime.java.JavaHookDefinition.execute(JavaHookDefinition.java:60)
  at cucumber.runtime.Runtime.runHookIfTagsMatch(Runtime.java:223)
  at cucumber.runtime.Runtime.runHooks(Runtime.java:211)
  at cucumber.runtime.Runtime.runBeforeHooks(Runtime.java:201)
  at cucumber.runtime.model.CucumberScenario.run(CucumberScenario.java:40)
  at cucumber.runtime.model.CucumberFeature.run(CucumberFeature.java:165)
  at cucumber.runtime.Runtime.run(Runtime.java:121)
  at cucumber.api.cli.Main.run(Main.java:36)
  at cucumber.api.cli.Main.main(Main.java:18)

为了达到这一点,我遵循了以下 2 个资源,但仍然没有成功:

罪魁祸首似乎是 Cucumber 集成到 Spring 中,因为当我使用普通 JUnit

@Test
方法尝试相同的方法时,模拟按预期工作。

那么你能告诉我我错过或误解了哪些 Cucumber 或 Spring 配置吗?

谢谢, 博格丹

java spring spring-boot cucumber mockito
4个回答
5
投票

@3wj 的方法对我有用。 在我的例子中,bean 可以选择性地注入到控制器中。看起来在这种情况下,@MockBean 将不起作用,我猜原因是 bean 没有被硬引用。添加额外的注解@Autowired 使bean 被硬引用,然后Spring 将初始化bean。

@RestController
public class MyController {

    public MyController(@Autowired(required = false) IMyService myService) {
        this.myService = myService;
    }

    @GetMapping("/the/path")
    public ResponseEntity<String> getData() {
        if(this.myService==null){
            //Throw service unavaillable exception
        }
        String data = this.myService.getData();
        return new ResponseEntity<>(data, HttpStatus.OK);
    }
}


@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class MyControllerIT {

    @Value("${local.server.port}")
    private int port;

    private RestTemplate restTemplate;

    @Autowired
    @MockBean
    private IMyService myService 

    @Test
    public void testQuery() throws Exception {
        // restTemplate to call rest API....
    }

}

3
投票

OK,所以我发现

@MockBean
注释被忽略了,因为我使用 Cucumber 运行测试而不是通过 Spring Boot 运行它们。呃...

所以我用

@MockBean
替换了
@Mock
,然后我手动将该模拟注入到我的服务层中。

所以现在我的测试看起来像这样:

@SpringBootTest(classes = CentralApp.class)
@ContextConfiguration
public class RefreshActiveJobsStepDefs {

  @Inject
  private SchedulerService schedulerService;

  @Mock
  private AgentRestClient agentRestClient;

  @Before
  public void setup() throws Exception {
   MockitoAnnotations.initMocks(this);
   given(agentRestClient.get(anyString())).willReturn("FINISHED");
   schedulerService.setAgentRestClient(agentRestClient);
  }
  //Skipping the actual Given-When-Then Cucumber steps...
}

如您所见,我还删除了

@CucumberOptions(glue=...)
注释,现在我确保通过运行器传递它,对于 CLI 来说,将使用
--glue
选项。

我希望这有帮助。


1
投票

@MockBean 没有被忽略,bean 被模拟但没有被注入,所以你可以通过@inject 注入它,这对我来说很好:

@Inject
@MockBean
private AgentRestClient agentRestClient;

1
投票

我在 2023 年遇到了这个问题并设法解决了它,这要归功于 GitHub 问题中的this comment

用@CucumberContextConfiguration 注释的类被传递给 springs TestContextManager 并受到与 JUnit 中的类相同的处理。因此,这应该会导致使用模拟的 MyService bean 创建应用程序上下文。

换句话说,您需要将

@MockBean
声明移动到
@CucumberContextConfiguration
类,然后将其注入您的步骤定义类(例如使用
@Autowired
)。

这是对我有用的代码:

@SpringBootTest
@CucumberContextConfiguration
@RunWith(Cucumber.class)
@CucumberOptions(
        plugin = {"pretty"},
        glue = "xxx",
        features = "classpath:xxx"
)
public class AcceptanceTestSuite {
    @MockBean MyMock mock;
}

然后

public class ScenarioStepDefinitions {
    @Autowired MyMock mock;

    // ... steps
}
© www.soinside.com 2019 - 2024. All rights reserved.