#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext-test.xml"})
#Transactional
public class MyServiceTest {
#Resource(name="myService")
public MyService myService;
#Test
public void testSeomthing() {
//do some asserts using myService.whatever()
}
}
However the tests are based on data I migrate in, so every time I run my suite of tests I want to execute my unrelated migration code. I don't want to run a #Before in each test class. I want to run it once at beginning of complete test process, where can I put this ?
I would advice you to create a test bean somewhere with startup logic invoked in #PostConstruct:
#Service
public class TestBean {
#PostConstruct
public void init() {
//startup logic here
}
}
Obviously this bean should only be created for tests, the easiest way to achieve this is to place it in src/test/java in a package that is component-scanned by Spring for #Service-annotated classes.
Note: you must remember that #PostConstruct is not running in a transaction! See How to call method on spring proxy once initialised.
JUnit also offers a #BeforeClass annotation which you can place on a static method to initialize resources just once.
Related
I have two Maven projects A and B, where project B is nested in project A. The structure looks like the following:
Project A:
src/test/java:
MyTest.java
Project B:
src/test/java:
MyNewTest.java
pom.xml
pom.xml
My goal is to let MyNewTest.java act as a wrapper of MyTest.java, and be able to invoke the test methods declared in MyTest.java from MyNewTest.java. MyTest has some injected dependencies.
My question is: how to initialize MyTest in MyNewTest to make sure that all the dependencies of MyTest are injected properly?
MyTest looks like the following:
public class MyTest {
#Autowired
Service service;
#Autowired
TestUtil util;
Info info;
#PostConstruct
void init() {
info = service.getStuff();
}
#Test
public test1() {
service.getMoreStuff();
// more code omitted
}
}
I have tried adding #Component to MyTest, and then in MyNewTest.java, use #Autowired like the following:
public class MyNewTest {
#Autowired
private MyTest baseTest;
#Test
public void runTest() {
// run the test in MyTest.java
baseTest.test1();
}
}
But this doesn't seem to work - baseTest is null. I also tried initializing MyTest by calling its default constructor, but the dependencies failed to be injected as expected. Can anyone suggest a proper way to initialize MyTest.java so that all its dependencies are injected as well? Thanks
The problem with your test is the #Autowired annotations as the Spring wiring isn't being triggered. There are two different paths you can take to fix this.
The first option is to manually instantiate baseTest in MyNewTest at which point it will no longer be null. However, tests will still fail because your two #Autowired dependencies will be null. You can either add setters or inject them via your constructor. Note- these classes should be mocked if you are performing Unit Tests. Here's how it would look if you chose to add these classes via the constructor -
private Service service;
private TestUtil util;
private MyTest baseTest;
#BeforeEach
void setUp() {
service = mock(Service.class);
util = mock(TestUtil.class);
baseTest = new MyTest(service, util);
}
The second option is to add configuration to your Test class to support the Spring wiring. I am not familiar with taking this route as I always choose the first option when possible. There are multiple ways to add the Spring wiring but the easiest is-
#SpringBootTest
#RunWith(SpringRunner.class)
public class MyNewTest {
However, this does not cover all use cases so you may need more specific configurations. You can find some of the possible options here - How to write JUnit test with Spring Autowire?
Edit- As I immediately recognized a problem, I didn't read the rest very carefully and I missed that these were Test classes you were trying to wire together. I am not sure if this is possible.
Only Spring beans can be #Autowired so the first step would be to attempt to add configuration to make your Test class into a Spring bean. I've never heard of this being done before but it would be easy to try. If not, you can get around this problem by using the first option.
The second problem is that tests are not included in the artifact. I'd imagine you could circumvent this issue by mixing your Test classes in with your regular classes but this is considered a bad practice. I've never heard of tests being dependent on other tests but I'd guess this is also a bad practice. What's the reason for wanting to create your Tests this way?
This method is empty in all my JUnit test cases. What is the use of this method?
Sonarqube is complaining
"Add a nested comment explaining why this method is empty, throw an UnsupportedOperationException or complete the implementation."
I can bypass this by adding some comment but I just want to know why it is necessary.
When you build a Spring boot application using Spring Initializer. It auto creates a test class for you with contextLoads empty method.
#SpringBootTest
class ApplicationContextTest {
#Test
void contextLoads() {
}
}
Note the use of #SpringBootTest annotation which tells Spring Boot to look for a main configuration class (one with #SpringBootApplication, for instance) and use that to start a Spring application context. Empty contextLoads() is a test to verify if the application is able to load Spring context successfully or not.
If sonarqube is complaining about the empty method then you can do something like this to verify your controller or service bean context:-
#SpringBootTest
public class ApplicationContextTest {
#Autowired
private MyController myController;
#Autowired
private MyService myService;
#Test
public void contextLoads() throws Exception {
assertThat(myController).isNotNull();
assertThat(myService).isNotNull();
}
}
Use different runner,
if you are using SpringRunner.class, use an alternative one such as MockitoJUnitRunner.class or MockitoJunitRunner.class rather then SpringRunner.class
#Runwith(SpringRunner.class)
#Runwith(JUnit4ClassRunner.class)
#Runwith(MockitoJUnit4Runner.class)
#Runwith(MockitoJUnitRunner.class)
I'm trying to test a spring batch job that performs a read (get data from another application) process (simple calculation) and write (into the mongodb)
the reader is #StepScope
here is the postConstruct of the read task.
#PostConstruct
public void init(){
employees.addAll(getListOfEmployeesBy(affectationMotifService.findAllRegistrationNumbers()));
}
public List<EmployeeForSalaryDTO> getListOfEmployeesBy(List<String> registrationNumbers){
LOG.debug("request to get all the employees by registration numbers {}" , registrationNumbers);
return coreResourceFeign.getAllEmployeesForSalaryByRegistrationNumbers(registrationNumbers).getBody();
}
When I try to launch the test of the job or what ever test in the application. spring always runs the init() of the read task .. which will fail the test because I need to mock the coreResourceFeign.getAllEmployeesForSalaryByRegistrationNumbers(registrationNumbers) .
I can't mock the method because it runs before the test begin.
here is the test
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {SalaryApp.class, SecurityBeanOverrideConfiguration.class})
public class SalaryJobServiceTest {
#Autowired
#InjectMocks
private SalaryJobService salaryJobService;
#Test
public void startJob() throws Exception {
SalaryJobDTO SalaryJobDTO = salaryJobService.start(Collections.emptyList());
Assert.assertNotNull(salaryJobDTO.getId());
}
}
I have no idea how to deal with spring batch tests. Any recommendation or help will be welcomed.
#PostConstruct will make sure your method is called immediately after the object is created. Spring application creates all the beans as per the configs while application start. This is expected behavior. If you do not want to call your method during application start up remove #PostConstruct and you can run your test mocking the dependent objects.
Rather you should use readers read method to load your data to the reader.
i have a spring boot application. sometimes i need to launch long running report generation. the easiest way was to create an #IntegrationTest to use spring's #Autowire:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#SpringApplicationConfiguration(classes=Application.class)
#IntegrationTest
public class ReportGenerationTest {
#Autowired MyService myService;
#Value("classpath:/test.txt") Resource testData; //it's in test, not in src
#Test
public void generate_report() {
report(myService, testData);
}
}
it works perfectly. but i don't want it to be run with every build. i also don't want to add/remove #Ignore as sooner or later someone will accidentally commit it without #Ignore and i don't want to do any code editing just to run reports
ideally i would like it to be a main method run on demand. but how can i manually create a spring context for the src service and test resources?
i need something like:
public class MyReport {
public static void main(String[] args) {
ReportGenerationTest reportGenerator = getAutowiredInstanceFromSpring();
reportGenerator.generate_report();
}
}
Is there a way to force the full rebuild of Spring context before the test?
Marking it with #DirtiesContext does not affect current test.
WHY: Cause before the test I set some System properties, that influence the context during build.
Sorry, I don't think so.
I haven't tried this, but one workaround might be to obtain the context in the #Before or #BeforeClass method, and call reload.
Your test should obtain the context (and cast it to ConfigurableApplicationContext), by following the test context guidance in the Spring Framework Reference.
Well, I managed to do this... But I warn you, it is dirty!
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { .... })
// order of test methods is important!
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class MyTest {
static {
System.setProperty("myprop", "myvalue");
}
/**
* Fake test that forces spring to reload context before real tests.
*/
#Test
#DirtiesContext
public void aaReloadContext() {
}
/**
* Fake test that clears system property after tests.
*/
#Test
public void zzClearProperty() {
System.clearProperty("myprop");
}
... real test methods...