3

I am writing below Spring Unit test code. Unit test @Before method is not getting executed. Since it is directly running @PostConstruct i am getting erorrs Caused by: java.lang.IllegalArgumentException: rate must be positive because the default value is 0.00. I want to set some value to request max limit so that postcontstruct block will go through smoothly. what is wrong in my code? Please help.

@Component
public class SurveyPublisher  {

    @Autowired
    private SurveyProperties surveyProperties;

    @PostConstruct
        public void init() {
            rateLimiter = RateLimiter.create(psurveyProperties.getRequestMaxLimit());
        }

    }

    public void publish() {
        rateLimiter.acquire();
        // do something
    }

}

//Unit test class

public class SurveyPublisherTest  extends AbstractTestNGSpringContextTests {

    @Mock
    SurveyProperties surveyProperties;

    @BeforeMethod   
    public void init() {
        Mockito.when(surveyProperties.getRequestMaxLimit()).thenReturn(40.00);
    }

    @Test
    public void testPublish_noResponse() {
        //do some test
    }

}
Kiran
  • 839
  • 3
  • 15
  • 45
  • how are you running test method from your test class? I don't you are using any `@RunWith(MockitoJUnitRunner.class)`. are you sure your mocks got injected in your the class you are testing? – Amit Naik May 13 '19 at 06:01
  • I have updated the test class.. it is extending `AbstractTestNGSpringContextTests` run as TestNG Test . – Kiran May 13 '19 at 06:11
  • 1
    The clean answer is to rewrite your `SurveyPublisher` to use constructor injection and also to inject the rate limiter, which will allow you to easily test those in your test case. – chrylis -cautiouslyoptimistic- May 13 '19 at 06:52
  • Create a `static` configuration class inside your test class. Create an `@Bean` method in there that returns the `SurveyProperties` just as you want them. You don't need a mock for this. Or depending on your test, don't use Spring and create the class and dependencies yourself. – M. Deinum May 13 '19 at 08:42

2 Answers2

2

Just realized it will always run postConstruct method before Junit callback methods cause spring takes the precedence. As explained in the documentation -

if a method within a test class is annotated with @PostConstruct, that method runs before any before methods of the underlying test framework (for example, methods annotated with JUnit Jupiter’s @BeforeEach), and that applies for every test method in the test class.

Solution to you issue -

  1. As @chrylis commented above refactor your SurveyPublisher to use constructor injection to inject the rate limiter. So you can then easily test.
  2. Inject Mock/Spy bean which is causing the problem
  3. Create test config to give you the instance of the class to use as @ContextConfiguration

    @Configuration
    public class YourTestConfig {
    
        @Bean
        FactoryBean getSurveyPublisher() {
            return new AbstractFactoryBean() {
                @Override
                public Class getObjectType() {
                    return SurveyPublisher.class;
                }
    
                @Override
                protected SurveyPublisher createInstance() {
                    return mock(SurveyPublisher.class);
                }
            };
        }
    }
    
Amit Naik
  • 983
  • 1
  • 5
  • 16
0

Here is the simple one worked.

@Configuration
@EnableConfigurationProperties(SurveyProperties.class)
    static class Config {
}


@ContextConfiguration(classes = {
        SurveyPublisherTest.Config.class })
@TestPropertySource(properties = { "com.test.survey.request-max-limit=1.00" })
public class SurveyPublisherTest extends AbstractTestNGSpringContextTests {

//Remove this mock
//@Mock
//SurveyProperties surveyProperties;
}
Kiran
  • 839
  • 3
  • 15
  • 45