How to autowire object to bean if class does not h

2019-08-27 23:57发布

问题:

I have so controller

@Controller
public class MyController {
 @Autowire
 MyClass myClass;
 //myClass doesn't have setter and getter
 .... 
 @RequestMapping("/path")
 public String underTest(){
    myClass.makeSomething();
    return "html.jsp"
}

I want make mock test using Mockito and mock myClass. In test class I want get myClass so:

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/BeanConfig.xml"); 
myClass = context.getBean("myClass ", MyClass .class);

But I need autowire this bean to Controller for testing controller's method(I think test code should not affect to normal code).

There are exist way to make it without writing of set method?

I want to check that myClass.makeSomething() invokes once in method underTest.

回答1:

I'm not sure I agree with you that test code should not affect normal code. I think an entirely valid reason to refactor / rewrite production code is to make it more testable (this is probably achieved by making it more modular, which is generally a good thing anyway).

This is precisely why annotations like

@VisibleForTesting

exist. Then you can create a package-local setter for MyClass, add the above annotation (for information to other programmers and possibly code inspection tools) and set the field in your test (which should reside in the same package).

Alternatively, since you are using Mockito, you could simply annotate the MyController instance with @InjectMocks, eg

@Test
public class MyControllerTest {

    @Mock
    private MyClass mockMyClass;
    @InjectMocks
    private MyController myController;

    @BeforeMethod
    public void before() {
        MockitoAnnotations.initMocks(this);
    }

    // do tests...
}

Note that @InjectMocks does not depend on any annotations on the target field (i.e. @Autowired, @Resource, @Inject etc). It just works. (Presumably you will still need those annotations for Spring injection, so don't remove them! The point is you can also use it for fields that aren't annotated).

Note also that, depending on which version of Mockito you are using, you may need to instantiate the MyController in the before() method before calling MockitoAnnotations.initMocks()



回答2:

As long as your test for MyController resides in the same package as MyController itself (as it's usually done - same packages in different source folders), you can simply assign it:

MyController controller = new MyController();
controller.myClass = mockMyClass;

That's the reason not to put @Inject/@Autowired on private fields.



回答3:

Try testing the controller directly with context.getBean(). MyClass will be autowired into it.



回答4:

I agree with @axtavt's answer, however if you absolutely want to go your way with injecting the mock in an integration test, you can do this:

define a overriding bean configuration file, say bean-test-config.xml, with content along these lines:

<import resource="classpath:spring/BeanConfig.xml"/>
<bean name="myClass" factory-method="mock" class="org.mockito.Mockito">
 <constructor-arg value="MyClass"></constructor-arg>
</bean>

This should correctly inject in a mock in your controller. You will have to get hold of this mock in your test and inject in any behavior that you are expecting from this mock though.