Mockito: Inject real objects into private @Autowired fields

后端 未结 5 2248
孤街浪徒
孤街浪徒 2020-11-29 17:56

I\'m using Mockito\'s @Mock and @InjectMocks annotations to inject dependencies into private fields which are annotated with Spring\'s @Autow

相关标签:
5条回答
  • 2020-11-29 18:22

    Mockito is not a DI framework and even DI frameworks encourage constructor injections over field injections.
    So you just declare a constructor to set dependencies of the class under test :

    @Mock
    private SomeService serviceMock;
    
    private Demo demo;
    
    /* ... */
    @BeforeEach
    public void beforeEach(){
       demo = new Demo(serviceMock);
    }
    

    Using Mockito spy for the general case is a terrible advise. It makes the test class brittle, not straight and error prone : What is really mocked ? What is really tested ?
    @InjectMocks and @Spy also hurts the overall design since it encourages bloated classes and mixed responsibilities in the classes.
    Please read the spy() javadoc before using that blindly (emphasis is not mine) :

    Creates a spy of the real object. The spy calls real methods unless they are stubbed. Real spies should be used carefully and occasionally, for example when dealing with legacy code.

    As usual you are going to read the partial mock warning: Object oriented programming tackles complexity by dividing the complexity into separate, specific, SRPy objects. How does partial mock fit into this paradigm? Well, it just doesn't... Partial mock usually means that the complexity has been moved to a different method on the same object. In most cases, this is not the way you want to design your application.

    However, there are rare cases when partial mocks come handy: dealing with code you cannot change easily (3rd party interfaces, interim refactoring of legacy code etc.) However, I wouldn't use partial mocks for new, test-driven & well-designed code.

    0 讨论(0)
  • 2020-11-29 18:22

    I know this is an old question, but we were faced with the same problem when trying to inject Strings. So we invented a JUnit5/Mockito extension that does exactly what you want: https://github.com/exabrial/mockito-object-injection

    EDIT:

    @InjectionMap
     private Map<String, Object> injectionMap = new HashMap<>();
    
     @BeforeEach
     public void beforeEach() throws Exception {
      injectionMap.put("securityEnabled", Boolean.TRUE);
     }
    
     @AfterEach
     public void afterEach() throws Exception {
      injectionMap.clear();
     }
    
    0 讨论(0)
  • 2020-11-29 18:26

    In Spring there is a dedicated utility called ReflectionTestUtils for this purpose. Take the specific instance and inject into the the field.

    
    @Spy
    ..
    @Mock
    ..
    
    @InjectMock
    Foo foo;
    
    @BeforeEach
    void _before(){
       ReflectionTestUtils.setField(foo,"bar", new BarImpl());// `bar` is private field
    }
    
    0 讨论(0)
  • 2020-11-29 18:30

    In Addition to @Dev Blanked answer, if you want to use an existing bean that was created by Spring the code can be modified to:

    @RunWith(MockitoJUnitRunner.class)
    public class DemoTest {
    
        @Inject
        private ApplicationContext ctx;
    
        @Spy
        private SomeService service;
    
        @InjectMocks
        private Demo demo;
    
        @Before
        public void setUp(){
            service = ctx.getBean(SomeService.class);
        }
    
        /* ... */
    }
    

    This way you don't need to change your code (add another constructor) just to make the tests work.

    0 讨论(0)
  • 2020-11-29 18:38

    Use @Spy annotation

    @RunWith(MockitoJUnitRunner.class)
    public class DemoTest {
        @Spy
        private SomeService service = new RealServiceImpl();
    
        @InjectMocks
        private Demo demo;
    
        /* ... */
    }
    

    Mockito will consider all fields having @Mock or @Spy annotation as potential candidates to be injected into the instance annotated with @InjectMocks annotation. In the above case 'RealServiceImpl' instance will get injected into the 'demo'

    For more details refer

    Mockito-home

    @Spy

    @Mock

    0 讨论(0)
提交回复
热议问题