I have a test class :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = IntegrationTestConfig.class)
#Transactional
#Component
public class AppTest {
#Autowired
private SimpleService simpleService;
#Test
public void test() {
simpleService.test();
}
}
I want make an aspect in order to monitor test method invocation
#Aspect
public class TestAspect {
#Pointcut("#annotation(org.junit.Test)")
public void pointcut() {
}
#After("pointcut()")
public void monitor() {
System.out.println("*** AFTER TEST ***");
}
}
Config :
#Configuration
#ImportResource("classpath:context.xml")
public class IntegrationTestConfig {
#Bean
public TestAspect testAspect() {
return new TestAspect();
}
}
But my monitor method has not been invoked, what's wrong? Generally - can I advice test methods?? I want to know which test method has been invoked.
JUnit itself has Rules that can be used for this.
Related
I have a service that has a DataProvider which I want to mock.
Problem: the service uses the data provider in #PostConstruct. But when I use #MockBean, the mocked values are not jet present in #PostConstruct.
What could I do?
#Service
public class MyService {
private List<Object> data;
#Autowired
private DataProvider dataProvider;
#PostConstruct
public void initData() {
data = dataProvider.getData();
}
public void run() {
System.out.println(data); //always null in tests
}
}
#SpringBootTest
public class Test {
#MockBean
private DataProvider dataProvider;
#Test
public void test() {
when(dataProvider.getData()).thenReturn(mockedObjects);
//dataProvider.init(); //this fixes it, but feels wrong
service.run();
}
}
IMHO unit testing MyService would be a better solution for this particular scenario (and I wouldn't feel wrong about calling initService manually in that case), but if you insist...
You could simply override the DataProvider bean definition for this particular test and mock it beforehand, sth like:
#SpringBootTest(classes = {MyApplication.class, Test.TestContext.class})
public class Test {
#Test
public void test() {
service.run();
}
#Configuration
static class TestContext {
#Primary
public DataProvider dataProvider() {
var result = Mockito.mock(DataProvider.class);
when(result.getData()).thenReturn(mockedObjects);
return result;
}
}
}
You might need to set spring.main.allow-bean-definition-overriding to true for the above to work.
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
}
}
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 -
As #chrylis commented above refactor your SurveyPublisher to use constructor injection to inject the rate limiter. So you can then easily test.
Inject Mock/Spy bean which is causing the problem
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);
}
};
}
}
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;
}
I am new to spring boot and i am trying to test a very simple class. But when i run the testMe() below I get exception below
java.lang.NullPointerException
at MyTest.testMe(MyTest.java:25)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
My understanding is when the context is loaded all the beans are initialized and object HelloWorld is created and are autowired in MyTest call. But helloWorld object is null at line helloWorld.printHelloWorld();
I need assistance here to understand what is missing.
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest(classes = {AppConfigTest.class})
public class MyTest {
#Mock
#Autowired
private Message myMessage;
#Autowired
private HelloWorld helloWorld;
#Test
public void testMe(){
helloWorld.printHelloWorld();
}
}
#Configuration
public class AppConfigTest {
#Bean
public HelloWorld helloWorld() {
return new HelloWorldImpl();
}
#Bean
public Message getMessage(){
return new Message("Hello");
}
}
public interface HelloWorld {
void printHelloWorld();
}
public class HelloWorldImpl implements HelloWorld {
#Autowired
Message myMessage;
#Override
public void printHelloWorld() {
System.out.println("Hello : " + myMessage.msg);
}
}
public class Message {
String msg;
Message(String message){
this.msg = message;
}
}
You're running your tests with a runner that's not Spring-aware, so no wiring is happening. Look at the Spring Boot testing documentation, all their examples use #RunWith(SpringRunner.class). To mock a bean, annotate it with #MockBean, not #Mock. Make sure that the spring-boot-starter-test is included in your POM.
I have a SpringBootApplication class that has a #PostConstruct method like that (it initializes the type of database connection):
#SpringBootApplication
public class SpringBootApp extends WebMvcConfigurerAdapter {
public static boolean workOffline = false;
private boolean setupSchema = false;
private IGraphService graphService;
private DbC conf;
#Autowired
public SpringBootApp(IGraphService graphService, DbC conf)
{
this.graphService = graphService;
this.conf = conf;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringBootApp.class, args);
}
#PostConstruct
public void initializeDB() {
if (workOffline) {
conf.setupOfflineEnvironment();
return;
}
else {
conf.setupProdEnvironment();
}
if (setupSchema) {
graphService.setupTestUsers();
}
}
}
I am also using Spring Boot Tests that extend this base class:
#RunWith(SpringRunner.class)
#Ignore
#SpringBootTest
public class BaseTest {
#Before
public void beforeTest() {
if (SpringBootApp.workOffline) {
conf.setupOfflineEnvironment();
} else {
conf.setupTestEnvironment();
}
graphService.setupTestUsers();}
#After
public void afterTest() {
graphService.deleteAllData();
}
}
My tests are under tests/ while my source code is under src/
Unfortunately there are cases that beforeTest() will execute before #PostConstuct and there are cases that it will execute after.. Is there a way to make my tests run with #SprinbBootTest without entering/constructiong the SpringBootApp class at all?
Thanks!
As requested, here's an attempt using spring properties (without an IDE handy, so please forgive any mistakes)
You can set a property for your test using SpringBootTest#properties
#RunWith(SpringRunner.class)
#Ignore
#SpringBootTest(properties="springBootApp.workOffline=true")
public class BaseTest {
#Before
public void beforeTest() { /* setup */ }
#After
public void afterTest() { /* teardown */ }
}
Now that we know that we can set the spring property in the test, we can set up the application to use the property:
#SpringBootApplication
public class SpringBootApp extends WebMvcConfigurerAdapter {
#Value("${springBootApp.workOffline:false}")
private boolean workOffline = false;
#Value("${springBootApp.setupSchema:false}")
private boolean setupSchema = false;
#PostConstruct
public void initializeDB() {
if (workOffline) {
// ...
} else {
// ...
}
if (setupSchema) {
// ...
}
}
}
As I've written this answer I've noticed a couple of things:
If you're only setting workOffline so you can run tests, then you probably just want to move the database setup actions out of the application and into the BaseTest class instead.
Same goes for setupSchema
If you are executing sql for your tests, don't forget about #Sql and #SqlGroup: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#__sql
Anyway, good luck!
I use SpringJUnit4ClassRunner for testing application, but I can not inject own test instance into other class instances. The reason of injecting class is that I need to prepare test class for testing.
How can I get/inject the test bean into rules/other classes form context in the Spring test?
Example :
Test class 1:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { AppTestConfig.class })
public class TestOne {
#Autoware
#Rule
public SimpleClassRule simpleClassRule;
#Test
public void test(){
//do something
}
}
Test class 2:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { AppTestConfig.class })
public class TestTwo {
#Autoware
#Rule
public SimpleClassRule simpleClassRule;
#Test
public void test(){
//do something
}
}
Rule:
public SimpleClassRule extend ExternalResource {
#Autoware
??? // need to have test class or TestClass.getClass() ???
#Override
protected void before() throws Throwable {
// need to have test class or TestClass.getClass() ???
}
}