How to disable @PostConstruct in Spring during Test
Asked Answered
F

5

16

Within a Spring Component I have a @PostConstruct statement. Similar to below:

@Singleton
@Component("filelist")
public class FileListService extends BaseService {

    private List listOfFiles = new Arrays.list();

    //some other functions


    @PostConstruct
    public void populate () {

        for (File f : FileUtils.listFiles(new File(SystemUtils.JAVA_IO_TMPDIR), new String[]{"txt"},true)){
            listOfFiles.add(f.getName());
        }   
    }

    @Override
    public long count() throws DataSourceException {
        return listOfFiles.size();
    }

    //  more methods .....
}

During Unit tests I would not like to have the @PostConstruct function called, is there a way to telling Spring not to do post processing? Or is there a better Annotation for calling a initiation method on a class durning non-testing ?

Farmland answered 5/11, 2012 at 11:34 Comment(5)
Is it FileListService you want to test or other classes depending on it?Abandon
The FileListService is required by a Web Service which is being tested - public class FileWSTest extends JerseyTest, which is using grizzly web containerFarmland
You can either mock it using a subclass or factor out the initialization part. You can place mock in package scanned only for tests and mark it as @Primary.Abandon
this (from mrembisz) worked - added the mock classes into the config directly with @Primary tag, and ensure that the injected/autowired were interface. one class override removing function and the other complete class mocked including the loading of database stuff, removed the need for a in-memory datbase during test ... how to give credit ?Farmland
I have reposted it as an answer.Abandon
A
7

Since you are not testing FileListService but a depending class, you can mock it for tests. Make a mock version in a separate test package which is scanned only by test context. Mark it with @Primary annotation so it takes precedence over production version.

Abandon answered 5/11, 2012 at 13:7 Comment(0)
G
8

Any of:

  1. Subclass FileListService in your test and override the method to do nothing (as mrembisz says, you would need to place the subclass in package scanned only for tests and mark it as @Primary)
  2. Change FileListService so the list of files is injected by Spring (this is a cleaner design anyway), and in your tests, inject an empty list
  3. Just create it with new FileListService() and inject the dependencies yourself
  4. Boot up Spring using a different configuration file/class, without using annotation configuration.
Gianina answered 5/11, 2012 at 11:39 Comment(1)
The FileListService is a @Component which is found by Spring using <context:annotation-config /> <context:component-scan base-package="com.app.ws" />, so overriding wouldn't stop spring from finding it. Also the some PostConstruct will actually do other things, so i would need to inject a function and not data. I'll guess i would need a way of injecting a Mocked class that overrides the exsisting bean-idFarmland
A
7

Since you are not testing FileListService but a depending class, you can mock it for tests. Make a mock version in a separate test package which is scanned only by test context. Mark it with @Primary annotation so it takes precedence over production version.

Abandon answered 5/11, 2012 at 13:7 Comment(0)
S
5

Declare a bean to override the existing class and make it Primary.

@Bean
@Primary
public FileListService fileListService() {
 return mock(FileListService.class);
}
Shoat answered 21/9, 2018 at 1:33 Comment(0)
C
0

check the profile as this:

  @PostConstruct
    public void populate () {
       if (!Arrays.asList(this.environment.getActiveProfiles()).contains("test")) {    
         for (File f : FileUtils.listFiles(new File(SystemUtils.JAVA_IO_TMPDIR), new 
              String[]{"txt"},true)){
            listOfFiles.add(f.getName());
          } 

        }           
    }
Cylinder answered 25/1, 2022 at 10:35 Comment(0)
M
0

I suggest you to create another bean (Service in this case) having exactly that @PostConstruct method.

In this way you are able to easily mock the new bean preventing the @PostConstruct method from being called. I've written a simple proof of concept in SpringBoot in order to make it more clear:

1) First we define our Service without @PostConstruct method

@Service
@RefreshScope
public class SomeService {
 
    public void doSomethingAfterPostConstruct() throws Exception  {
        // ...
    }

}

2) Then we define another service with the @PostConstruct method we need for the first service

@Service
public class PostConstructService {

    @Autowired
    SomeService someService;

    @PostConstruct
    public void doSomething() throws Exception {
        someService.doSomethingAfterPostConstruct();
    }
}

3) In the end we can easily mock the @PostConstruct method by mocking its own class

/** Test class to extend **/
@AutoConfigureMockMvc
public abstract class AbstractTestConfig {

    @MockBean
    PostConstructService postConstructServiceMocked;

}

Hope this helps you! :)

Monocyclic answered 7/12, 2023 at 17:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.