英文:
Unable to wire in dependency using MockBean in WebMVCTest
问题
以下是您提供的代码的中文翻译部分:
我有一个控制器类:
public class Controller {
private final IProcessor processor;
public Controller(final ProcessorFactory factory) {
this.processor = factory.getInstance();
}
}
一个工厂类,用于提供不同的 IProcessor 实例:
@Component
public class ProcessorFactory {
private final Dep1 dep1;
private final Dep2 dep2;
public ProcessorFactory(final Dep1 dep1, final Dep2 dep2) {
this.dep1 = dep1;
this.dep2 = dep2;
}
public IProcessor getInstance() {
if (...) {
return new ProcessorA(dep1, dep2);
}
return new ProcessorB(dep1, dep2);
}
}
在我的 Mockito 测试类中,我使用 Junit5,但无法实例化 IProcessor 成员,它为空:
@WebMvcTest(Controller.class)
public class ControllerTest {
@MockBean
private ProcessorFactory processorFactory;
@MockBean
private IProcessor processor;
@Autowired
private MockMvc mockMvc;
@Test
public void test1() throws Exception {
when(processor.process(any(Request.class), any(String.class)))
.thenReturn(new BlaBla("Test", "Test"));
String request = ...
this.mockMvc.perform(post("/test/test").contentType(MediaType.APPLICATION_JSON).content(request))
.andDo(print())
.andExpect(status().is2xxSuccessful());
}
}
请注意,您需要在代码中的 if (...)
部分填写适当的条件,以便根据条件返回适当的 Processor 实例。
英文:
I have a controller class:
public class Controller {
private final IProcessor processor;
public Controller (final ProcessorFactory factory) {
this.processor = factory.getInstance();
}
}
A Factory class to provide the different instances of IProcessor:
@Component
public class ProcessorFactory {
private final Dep1 dep1;
private final Dep2 dep2;
public ProcessorFactory (final Dep1 dep1,
final Dep2 dep2) {
this.dep1= dep1;
this.dep2= dep2;
}
public IProcessor getInstance() {
if (...) {
return new ProcessorA(dep1, dep2);
}
return new ProcessorB(dep1, dep2);
}
}
In my mockito test class where I use Junit5, I am not able to instantiate the IProcessor
member and is null:
@WebMvcTest(Controller.class)
public class ControllerTest {
@MockBean
private ProcessorFactory processorFactory ;
@MockBean
private IProcessor processor;
@Autowired
private MockMvc mockMvc;
@Test
public void test1() throws Exception {
when(processor.process(any(Request.class), any(String.class)))
.thenReturn(new BlaBla("Test", "Test"));
String request = ...
this.mockMvc.perform(post("/test/test").contentType(MediaType.APPLICATION_JSON).content(request))
.andDo(print())
.andExpect(status().is2xxSuccessful());
}
}
I am not sure I am using MockBean correctly. Basically I want to mock both the Factory and the Processor.
答案1
得分: 2
Here's the translated content:
由于您需要在Spring上下文初始化期间(在Controller
的构造函数内部)调用模拟的方法(getInstance()
),您需要以不同的方式模拟该方法。模拟的bean不仅必须作为现有对象提供,而且还必须定义其模拟的行为。
另外,IProcessor
的实现未配置为Spring bean,因此Spring不会注入它们 - ProcessorFactory
显式调用 new
并在没有Spring参与的情况下创建对象。
我已创建了一个简单的项目来复现您的问题并提供解决方案 - 如果您想检查整个工作流程是否正常,请在GitHub上查看此处,但这里是最重要的测试代码段(我稍微简化了方法):
@WebMvcTest(Controller.class)
class ControllerTest {
private static final IProcessor PROCESSOR = mock(IProcessor.class);
@TestConfiguration
static class InnerConfiguration {
@Bean
ProcessorFactory processorFactory() {
ProcessorFactory processorFactory = mock(ProcessorFactory.class);
when(processorFactory.getInstance())
.thenReturn(PROCESSOR);
return processorFactory;
}
}
@Autowired
private MockMvc mockMvc;
@Test
void test1() throws Exception {
String result = "this is a test";
when(PROCESSOR.process(any()))
.thenReturn(result);
mockMvc.perform(post("/test/test")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(content().string(result));
}
}
英文:
Since you need to call a mocked method (getInstance()
) during Spring context initialization (inside the Controller
's constructor), you need to mock the said method in a different way. The mocked bean has to be not only provided as an existing object, but also it should have it's mocked behavior defined.
Addtionally, IProcessor
implementations are not configured as Spring beans, so Spring will not inject them - ProcessorFactory
calls new
explicitly and creates the objects without Spring involvement.
I've created a simple project to reproduce your problem and provide a solution - you can find it here on GitHub if you want to check if the whole thing is working, but here's the most important test snippet (I've simplified the methods a bit):
@WebMvcTest(Controller.class)
class ControllerTest {
private static final IProcessor PROCESSOR = mock(IProcessor.class);
@TestConfiguration
static class InnerConfiguration {
@Bean
ProcessorFactory processorFactory() {
ProcessorFactory processorFactory = mock(ProcessorFactory.class);
when(processorFactory.getInstance())
.thenReturn(PROCESSOR);
return processorFactory;
}
}
@Autowired
private MockMvc mockMvc;
@Test
void test1() throws Exception {
String result = "this is a test";
when(PROCESSOR.process(any()))
.thenReturn(result);
mockMvc.perform(post("/test/test")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(content().string(result));
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论