Java annotations - code simplifications - java

I am looking for a way to simplify the following code.
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {
// My configuration classes
})
public class MyServiceTest {
#Autowired
private MyService service;
#Test
public void myTest() {
Assert.assertTrue(service != null);
}
}
I have many configuration classes and I don't want to put them into each test class. So I got the idea to create my own annotation:
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {
// My configuration classes
})
public #interface IntegrationTests {
}
I try to use it in the following way:
#IntegrationTests
public class MyServiceTest {
#Autowired
private MyService service;
#Test
public void myTest() {
Assert.assertTrue(service != null);
}
}
But it doesn't work. Any idea?

You can place these annotation on a superclass:
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {
// My configuration classes
})
public abstract class AbstractIntegrationTest { ... }
.
public class MyServiceTest extends AbstractIntegrationTest { ... }
This approach also allows you to declare common #Autowired dependencies in the base class and customize #ContextConfiguration classes in concrete tests.

The reason your custom composed annotation did not work is that JUnit does not support #RunWith as a meta-annotation. Thus, when you compose your annotation as follows...
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { /* configuration classes */ })
public #interface IntegrationTests {
}
... it is JUnit that cannot see that you want to use the SpringJUnit4ClassRunner.
Spring Framework 4.0 and greater should have no problem seeing the declarations of #WebAppConfiguration and #ContextConfiguration used as meta-annotations.
In other words, the following should work for you:
#WebAppConfiguration
#ContextConfiguration(classes = { /* configuration classes */ })
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface IntegrationTests {
}
#RunWith(SpringJUnit4ClassRunner.class)
#IntegrationTests
public class MyServiceTest {
#Autowired
private MyService service;
#Test
public void myTest() {
assertNotNull(service);
}
}
As an alternative, you can use an abstract base test class as recommended by axtavt.
Regards,
Sam (author of the Spring TestContext Framework)

Related

Unit testing with SpringJUnit4ClassRunner using only #Component and #Autowired

I have the following class:
#Component
public class MyClass {
#Autowired MyPojo pojo;
}
How do i test it without mocking the injected beans? I do not have a configuration [XML or declarative].
I have done the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ComponentScan
public class MyClassTest {
#Autowired MyClass myClass;
#Test
public void test() {
this.myClass...()
}
}
If you do not want use any type of configuration, neither Java nor XML config, you can use #ContextConfiguration with your component classes listed:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyPojo.class, MyClass.class })
public class MyClassTest {
#Autowired
private MyClass myClass;
#Test
public void test() {
// myClass...
}
}
Please note that MyPojo class should also be annotated with #Component.
However, in the real life scenario you probably will need at least one #Configuration class (which can be also used with #ContextConfiguration).
Please refer Spring Documentation for more information about Spring integration tests support.

Junit 5 test, getting java.lang.IllegalStateException: Test classes cannot include #Bean methods

#ContextConfiguration(classes = ConfigureCustomConfigurationModelProviderTest.class)
public class ConfigureCustomConfigurationModelProviderTest extends AbstractContextTest {
#Bean(name = "smth")
public static ConfigurationModelProvider get() {
return AnnotationConfigurationModelProvider.getInstance();
}
/*...*/
}
I'm getting this error since migrating from junit4 to junit5. Why?
You should move every beans to #Configuration class for example TestConfig:
#Configuration
public class TestConfig {
#Bean(name = "smth")
public static ConfigurationModelProvider get() {
return AnnotationConfigurationModelProvider.getInstance();
}
}
and import it via #Import:
#Import({TestConfig.class})
#ContextConfiguration(classes = ConfigureCustomConfigurationModelProviderTest.class)
public class ConfigureCustomConfigurationModelProviderTest extends AbstractContextTest {
}
To add to Amir's answer: if you only need the bean for some tests you can omit the #Configuration annotation at the top.

Exclude ApplicationStartup Event listener when testing

I recently added an ApplicationStartup class to my SpringBoot project
#Component
public class ApplicationStartup
implements ApplicationListener<ApplicationReadyEvent> { ...
It implements ApplicationListener.
Now when I run my old JUNit tests that have nothing to do with that class, The testrunner tries to Run my StartupListener, which is neither necessary not appropriate in these cases.
How do I skip the ApplicationListener when my tests initialize?
#RunWith(SpringRunner.class)
#SpringBootTest
public class SubmissionItemManagerTest {...
You can mock your ApplicationStartup class
Add this declaration to your test case:
#MockBean
private ApplicationStartup applicationStartup
This will create a mocked instance of ApplicationStartup and mark it as #Primary in your test context thereby replacing the actual instance ofApplicationStartup.
You can create a separate application class for testing and exclude the components that are not required for tests:
#SpringBootApplication
#ComponentScan(excludeFilters = #ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
value = { ApplicationStartup.class,
RealApplication.class }))
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
Then in your SubmissionItemManagerTest class use the TestApplication class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestApplication.class)
public class SubmissionItemManagerTest {
...
}

How can I use default configuration and default test listeners for all my Spring tests?

I have several dozen tests that all use the same configuration and listeners for all tests. This means the follow lines are repeated:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = WebDriverConfig.class)
#TestExecutionListeners(listeners = {ScreenshotTaker.class, DependencyInjectionTestExecutionListener.class})
I've created the following:
public class WebDriverRunner extends SpringJUnit4ClassRunner {
public WebDriverRunner(Class<?> clazz) throws InitializationError {
super(clazz);
}
#Override
protected TestContextManager createTestContextManager(Class<?> clazz) {
return super.createTestContextManager(ConfigShim.class);
}
#ContextConfiguration(classes = WebDriverConfig.class)
#TestExecutionListeners(listeners = {ScreenshotTaker.class, DependencyInjectionTestExecutionListener.class})
public static class ConfigShim {
}
}
Which means I can run tests as follows:
#RunWith(WebDriverRunner.class)
public class ShoppingCartPageIT {
But, this changes the names of the test.
If you use Spring Framework 4.1 or higher, you can create your own composed annotation for testing configuration:
Define your annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#ContextConfiguration(classes = WebDriverConfig.class)
#TestExecutionListeners({
ScreenshotTaker.class,
DependencyInjectionTestExecutionListener.class
})
public #interface WebDriverTestConfig {}
and then annotate your tests:
#RunWith(SpringJUnit4ClassRunner.class)
#WebDriverTestConfig
public class ShoppingCartPageIT { /* ... */ }

How to load beans in inherited XML config, before beans in Configuration classes (integration tests in Spring)

When writing integration tests in Spring, how can I ensure that all the beans in my parent context are loaded before any others?
Currently, the context classes further down my hierarchy are being loaded too early and it's causing null pointer exceptions.
I am mixing XML and #Configuration classes
My set up
I have an abstract test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/persistence-context.xml"}) <-- My persistence layer contains my DAO classes
public abstract class MyBaseTest implements ApplicationContextAware { ... }
...and my actual tests are in a concrete class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextHierarchy({
#ContextConfiguration(classes = TestConfig.class)
})
public class MyTest extends MyBaseTest {
#Autowired
MyDao myDao; // when I debug my code, I can see that this does not stay null - it gets loaded eventually!
...
}
... which needs beans from my TestConfig class
#Import(value = {AnotherConfigClass.class})
#Configuration
public class TestConfig {
#Autowired
MyDAO myDAO; <-- this is null when loading classes
#Bean
public MyService myService() {
MyService myService = new MyService();
myService.setDAO(myDAO); <-- my DAO is null at this point!
return myService;
}
...
}
... which needs beans from my AdditionalConfig class
#Configuration
public class AnotherConfigClass {
...
#PostConstruct
public void init() {
myService.doSomethingUsingMyDAO(); <--- I need to use myService here, but a NPE is thrown.
AFAIK, annotations on base classes are not inherited on concrete classes. You have to explicitely load /persistence-context.xml:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextHierarchy({
#ContextConfiguration(locations = {"/persistence-context.xml"}),
#ContextConfiguration(classes = TestConfig.class)
})
public class MyTest extends MyBaseTest {
...

Categories