Spring @MockBean 未注入 Cucumber
Spring @MockBean not injected with Cucumber
我正在实施一个 SchedulerService
,它使用 AgentRestClient
bean 从外部系统获取一些数据。它看起来像这样:
@Service
public class SchedulerService {
@Inject
private AgentRestClient agentRestClient;
public String updateStatus(String uuid) {
String status = agentRestClient.get(uuid);
...
}
...
}
为了测试这个服务,我正在使用 Cucumber,同时我试图使用 Spring Boot 的 @MockBean
注释来模拟 AgentRestClient
的行为,如下所示:
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
永远不会 mocked/injected。 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.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 个资源,但仍然没有成功:
- https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4
- http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/MockBean.html
罪魁祸首似乎是 Cucumber 集成到 Spring 中,因为当我尝试使用普通 JUnit @Test
方法的相同方法时,模拟按预期工作。
那么你能告诉我我错过或误解了什么 Cucumber 或 Spring 配置吗?
谢谢,
博格丹
好的,所以我发现 @MockBean
注释被忽略了,因为我是 运行 使用 Cucumber 而不是 运行 通过 Spring Boot 进行测试。呃...
所以我用 @Mock
替换了 @MockBean
,然后我手动将该模拟注入到我的服务层。
所以现在我的测试是这样的:
@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
选项。
希望对您有所帮助。
@MockBean 没有被忽略,bean 被模拟但没有被注入,所以你可以通过@inject 注入它,这对我来说很好:
@Inject
@MockBean
private AgentRestClient agentRestClient;
@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....
}
}
我正在实施一个 SchedulerService
,它使用 AgentRestClient
bean 从外部系统获取一些数据。它看起来像这样:
@Service
public class SchedulerService {
@Inject
private AgentRestClient agentRestClient;
public String updateStatus(String uuid) {
String status = agentRestClient.get(uuid);
...
}
...
}
为了测试这个服务,我正在使用 Cucumber,同时我试图使用 Spring Boot 的 @MockBean
注释来模拟 AgentRestClient
的行为,如下所示:
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
永远不会 mocked/injected。 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.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 个资源,但仍然没有成功:
- https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4
- http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/MockBean.html
罪魁祸首似乎是 Cucumber 集成到 Spring 中,因为当我尝试使用普通 JUnit @Test
方法的相同方法时,模拟按预期工作。
那么你能告诉我我错过或误解了什么 Cucumber 或 Spring 配置吗?
谢谢, 博格丹
好的,所以我发现 @MockBean
注释被忽略了,因为我是 运行 使用 Cucumber 而不是 运行 通过 Spring Boot 进行测试。呃...
所以我用 @Mock
替换了 @MockBean
,然后我手动将该模拟注入到我的服务层。
所以现在我的测试是这样的:
@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
选项。
希望对您有所帮助。
@MockBean 没有被忽略,bean 被模拟但没有被注入,所以你可以通过@inject 注入它,这对我来说很好:
@Inject
@MockBean
private AgentRestClient agentRestClient;
@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....
}
}