Configure Spring Boot Test to use separate sets of beans - java

I want for my Spring Boot test to use new instances for beans for each test run. Which part of the test configuration can I set it?

You may wanna take a look at the #DirtiesContext annotation. Once state of the bean will be modified, new context will be provided:
There are few options to run with, namely:
Before current test class: when declared at the class level with class mode set to BEFORE_CLASS
Before each test method in current test class: when declared at the class level with class mode set to BEFORE_EACH_TEST_METHOD
Before current test method: when declared at the method level with method mode set to BEFORE_METHOD
After current test method: when declared at the method level with method mode set to AFTER_METHOD
After each test method in current test class: when declared at the class level with class mode set to AFTER_EACH_TEST_METHOD
After current test class: when declared at the class level with class mode set to AFTER_CLASS
for further reading, please take a look at:
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/annotation/DirtiesContext.html

You can use #DirttiesContext like pezetem wrote.
But if you must have fresh instances of beans for test try refactor your tests and maybe code because you probably will have problems with working app.

Related

How to swap a resource file path for a test file during unit testing?

I have a resource file which has some settings. I have a ResourceLoader class which loads the settings from this file. This class is currently an eagerly instantiated singleton class. As soon as this class loads, it reads the settings from the file (file path stored as a constant field in another class). Some of these settings are not suitable for unit tests. For example, I have thread sleep time in this file, which may be hours for production code, but I'd like it to be a couple of milliseconds for unit tests. So I have another test resource file which has a different set of values.
How do I go about swapping the main resource file with this test file during unit testing? The project is a Maven project and I'm using TestNG as the testing framework. These are some of the approaches I've been thinking about but none of them seem ideal:
Use #BeforeSuite and modify the FilePath constant variable to point to the test file and use #AfterSuite to point it back to the original file. This seems to be working, but I think because the ResourceLoader class is eagerly instantiated, there is no guarantee that the #BeforeSuite method will always execute before the ResourceLoader class is loaded and hence old properties may be loaded before the file path is changed. Although most compilers load a class only when it is required, I'm not sure if this is a Java specification requirement. So in theory this may not work for all Java compilers.
Pass the resource file path as a command line argument. I can add the test resource file path as command line argument in the surefire configuration in the pom. This seems a bit excessive.
Use the approach in 1. and make ResourceLoader lazy instantiated. This guarantees that if #BeforeMethod is called before the first call to ResourceLoader.getInstance().getProperty(..), ResourceLoader will load the correct file. This seems to be better than the first 2 approaches, but I think making a singleton class lazy instantiated makes it ugly as I can't use a simple pattern like making it an enum and such (as is the case with eager instantiation).
This seems like a common scenario, what is the most common way of going about it?
All singletons either eagerly or lazily instantiated are anti-pattern. Usage of singletons makes unit testing harder because there is no easy way to mock singleton.
Mock static method
A workaround is to use PowerMock to mock static method returning singleton instance.
Use dependency injection
A better solution is to use dependency injection. If you already use a dependency injection framework (e.g. Spring, CDI), refactor the code to make ResourceLoader a managed bean with scope singleton.
If you don't use a dependency injection framework, an easy refactoring will be to make changes to all classes using the singleton ResourceLoader:
public class MyService {
public MyService() {
this(ResourceLoader.getInstance());
}
public MyService(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
And then in unit tests mock ResourceLoader using Mockito
ResourceLoader resourceLoader = mock(ResourceLoader.class);
when(ResourceLoader.getProperty("my-property")).thenReturn("10");
MyService myService = new MyService(resourceLoader);
Externalise configuration
Another approach is to place a file with test settings under src/test/resources.
If you store settings in the src/main/resources/application.properties, a file src/test/resources/application.properties will override it.
Also, externalising configuration to a file not packaged in a JAR is a good idea. This way, file src/main/resources/application.properties will contain default properties and a file passed using command-line parameter will override these properties. So, a file with test properties will be passed as a command line parameter too. See how Spring handles externalised configuration.
Use Java System Properties
Even easier approach is to allow overriding of default properties with System Properties in the method ResourceLoader.getInstance().getProperty() and pass test properties this way
public String getProperty(String name) {
// defaultProperties are loaded from a file on a file system:
// defaultProperties.load(new FileInputStream(new File(filePath)));
// or from a file in the classpath:
// defaultProperties.load(ResourceLoader.class.getResourceAsStream(filePath));
return System.getProperty(name, defaultProperties.get(name));
}
Check if you in jUnit
You can also check in runtime whether jUnit is running and then exchange the path. That would work like this (untested):
public static funktionToTest() {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
List<StackTraceElement> list = Arrays.asList(stackTraceElements);
String path = "/origin/path/to/your/file"; // here can you set the default path to your rescource
for (StackTraceElement stackTraceElement : list) {
if (stackTraceElement.getClassName().startsWith("org.junit.")) {
path = "/path/only/in/jUnit/test"; // and here the normal path
}
}
//do what you want with the path
}

Merge multiple #TestExecutionListeners from different declarations?

For the integration tests of my Spring Boot app, I have declared custom meta-annotations (a bit like Spring Boot's test slice annotations). How can I declare different TestExecutionListeners in each meta-annotation, and have all of them merged when running a test class?
I can only find mergeMode = MERGE_WITH_DEFAULTS which merges the declared TestExecutionListener with the default ones, but not different custom Listeners declared in different places.
A minimal example:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#TestExecutionListeners(
listeners = DbTestListener.class,
mergeMode = MERGE_WITH_DEFAULTS)
public #interface DbIntegrationTest {
}
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#TestExecutionListeners(
listeners = MessagingTestListener.class,
mergeMode = MERGE_WITH_DEFAULTS)
public #interface MessagingIntegrationTest {
}
#RunWith(SpringRunner.class)
#DbIntegrationTest
#MessagingIntegrationTest
public class ExampleTest {
// test cases here
}
As a result, I'd like to have both custom TestListeners and the default ones executed for my ExampleTest.
Clarification: the above example is minimal as to show what I want. Of course like that it doesn't make much sense. My own composed annotations have much more setup in them which I haven't shown, and I have multiple layers of meta-annotations.
There is no such features in the current version of spring test.
If you think more deeply about your ideas, it will introduce ambiguous in some cases. For example, if #DbIntegrationTest and #MessagingIntegrationTest are configured with different values of mergeMode or inheritListeners, which value should the framework use?
The current behaviour is that if there are multiple #TestExecutionListeners or its meta-annotation are marked , only the topmost declared one will take effect while the other will be ignored.
Some ideas which may help you to achieve the similar result :
Use a meta-annotation to predefine some kind of profile which groups several TestExecutionListeners that will be used together in different test scenario. Just annotate this single meta-annotation to the test class. For example :
#MessagingTest contains all the required listeners for messaging integration test :
#TestExecutionListeners(
listeners = {SetupMessageBrokerListener.class, FooBarListener.class } ,
mergeMode = MERGE_WITH_DEFAULTS)
public #interface MessagingTest {
}
#DatabaseTest contains all the required listeners for DB integration test :
#TestExecutionListeners(
listeners = {SetupDatabaseListener.class, CreateTestingDataListener.class , FooBarListener.class } ,
mergeMode = MERGE_WITH_DEFAULTS)
public #interface DatabaseTest{
}
Implement your own version of TestContextBootstrapper. What TestExecutionListener to be created is defined in its getTestExecutionListeners(). Use #BootstrapWith to activate this TestContextBootstrapper. AbstractTestContextBootstrapper is the good starting point . Basically , you need to add your customised logic at the end of getTestExecutionListeners() . Sadly, this method is finalised which does not allow to override in the sub-classes...
A possible solution is to register all TestExecutionListeners in META-INF/spring.factories, and write each Listener so that it only does something if some annotation (or other marker) is present on the test class or in the test context.
For usage of spring.factories see: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext-tel-config-automatic-discovery
The disadvantage of this solution is that the setup of TestExecutionListeners (which are defined and which will be active) is a bit "hidden" or spread out in the code base.
What do these listeners do? If provide execution environment then you misuse listener API.
To configure (and cleanup) environment you should use
#Rule \ #ClassRule if you use Junit4
#ExtendWith if you use Junit5
These are easily combined within single test.

Calling a static method on a variable rather than class

So I am writing some Scala tests for some Java code (I am very new at both of them). What I am trying to do is instead of calling .create on the Configuration class, I want to call create on the conf mock object. How can I go about doing this?
When I try to call .create on a mock of the Configuration class, I get an error since (I think) .create is a static method from a custom abstract Configuration java class
Scala test code:
var conf: Configuration = mock[Configuration]
when(Configuration.create("var1","var2", "var3")).thenReturn(testConfiguration)
Thanks!

Is there a way to replace class extension in groovy/java?

My application is an automation testing framework written in groovy/java.
Currently I have 2 variants of its usage:
1) All my tests classes extends from BaseTest class which reads properties, do some other staff like setup and cleanup, but in the main it starts SpringBoot context where the main application to be tested works. It woks perfect, but every time I run any test - spring starting first which is takes up to a minute of my time.
2) I have another class to extend from called ProxyBaseTest class which creates proxy to a running instance of main application (in case I started it manually with gradle in a separate window). It also works good.
The main problem for me is to replace all the time
class SomeTestClass extends BaseTest {
with
class SomeTestClass extends ProxyBaseTest {
and vise versa.
Is there a way to do something like that:
class SomeTestClass extends A {
....
}
Class A {
....
if(applicationIsRunning){
// use/return/extend/replace/??? from ProxyBaseTest
}else{
// use/return/extend/replace/??? from BaseTest
}
}
I can't merge BaseTest and ProxyBaseTest because BaseTest using springbeans, spring context, annotations etc... if I'll be able to merge it - it is going to make anyone mad after.
3 different ways to do it. The key words to look for are:
Annotations: Don't subclass from a base test class. Annotate the class instead. E.g. jUnit went from subclassing to annotations.
Delegation: Your base class A is an example, where the use/return/extend/replace/??? is a method call to the class actually doing the work.
Composition: Instead of subclassing from a Test class, the code should obtain a reference to the appropriate test class and store it as a field, so you can call whatever method you need. The two test class implementations should then implement a common interface, which is the type of the field.

How to inject a property from properties file to a non-managed class in Spring?

I would like to set a property to a class that is not managed by Spring. The actual case is as follows: I have a regex which I want to externalize to the properties file. So far what I have tried is to create a CommandLineRunner, and read from properties file and set the regex to whatever field I want. But I wonder if there's an out of the box way to do so.
You might do one of the following:
Make the class a "normal" Java class where the regex is passed in via constructor or method parameter by the Spring managed class who uses it.
Give the class a static field "regex" (with a default value perhaps) that gets set by a Spring bean via a setter on startup (#PostConstruct).
Give the class a static field "regex" (with a default value perhaps) that gets set using a static block in your class reading from the properties file (if it is available on the classpath!).
Make the class a "normal" Singleton (with default values perhaps) that gets initialized on Spring startup (https://www.baeldung.com/running-setup-logic-on-startup-in-spring)

Categories

Resources