8

Why when I injecting mocks via Mockito my @Postconstruckt method is not calling?

@Service
public class MyService {
    public MyService() {
        System.out.println("CONSTRUKTOR");
    }

    @PostConstruct
    public void init() {
        System.out.println("POST CONSTRUCT");
    }

@RunWith(MockitoJUnitRunner.class)
public class Mockito1 {

    @InjectMocks
    private MyService service;

    @Before
    public void init() {
    }

Output: Only: CONSTRUKTOR

Jan Testowy
  • 649
  • 3
  • 13
  • 32
  • What are you looking to get out of this mock? Are you using this service somewhere else or are you actually testing the service? – Makoto Sep 25 '18 at 16:56
  • I am just going wondering how mockito treats @postconstruct. I am expecting something like: CONSTRUCTOR, POST CONSTRUCT – Jan Testowy Sep 25 '18 at 17:12
  • 1
    Why would it do anything with that? You're mocking (e.g. faking) the Spring-driven interactions with this object, so the Spring construct of `@PostConstruct` wouldn't even apply. So again, what is your goal with this mock? Do you want to test this service or do you want to use a mock of your service somewhere else? – Makoto Sep 25 '18 at 17:26

3 Answers3

5

Because PostConstruct is only spring concept. But you can call postConstruct manually.

@Before
public void prepare() {
    MockitoAnnotations.initMocks(this);
    this.service.init(); //your Injected bean
}
shiramy
  • 789
  • 8
  • 13
4

I modified a little your service by adding a method foo:

@Service
public class MyService {
    public MyService() {
        System.out.println("CONSTRUKTOR");
    }

    @PostConstruct
    public void init() {
        System.out.println("POST CONSTRUCT");
    }

    public String foo() {
        return "bar";
    }
}

If you want to get behaviour, that you described, there are at least two possibilities:

  1. @RunWith(SpringJUnit4ClassRunner.class) + @Autowired - that combination will let you to get a usual service in your test

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = MyService.class)
    public class Mockito1 {
    
        @Autowired
        private MyService service;
    
        @Before
        public void init() {
        }
    
        @Test
        public void name() throws Exception {
            System.out.println(service.foo());
        }
    }
    

This code will print:

CONSTRUKTOR
POST CONSTRUCT
bar
  1. @RunWith(SpringJUnit4ClassRunner.class) + @SpyBean - that combination will let you to get a service in your test and to modify it's behaviour using Mockito

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = MyService.class)
    public class Mockito1 {
    
        @SpyBean
        private MyService service;
    
        @Before
        public void init() {
        }
    
        @Test
        public void name() throws Exception {
            System.out.println(service.foo());
            Mockito.when(service.foo()).thenReturn("FOO");
            System.out.println(service.foo());
        }
    }
    

This code will print:

CONSTRUKTOR
POST CONSTRUCT
bar
FOO
0

@PostConstruct is an annotation defined by JSR 250 and it will be ignored in your current test cause you are using @RunWith(MockitoJUnitRunner.class), i.e., A JUnit runner that is not aware of this annotation. If you are writing unit tests they should be simple ad just test the business logic of your application, but if you want to write integrations tests that use some third-party code that is able to process this kind of annotation, like Spring, for example. Then you can annotate your test class with following annotations:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppConfig.class})
Diego Marin Santos
  • 1,923
  • 2
  • 15
  • 29