Unable to wire in dependency using MockBean in WebMVCTest.

huangapple go评论129阅读模式
英文:

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));
}
}

huangapple
  • 本文由 发表于 2020年8月13日 01:44:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/63382047.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定