Autowire auto-configured dependency in integration test - spring

As part of my Spring Boot app, I have a Thymeleaf HTML template rendering service:
#Service
class TemplateService(private val templateEngine: SpringTemplateEngine) {
fun renderTemplate(templateName: String, vars: Map<String, Any>): String =
templateEngine.process(templateName, Context()
}
A method in this service calls templateEngine.process. I would like to test renderTemplate on a concrete template to make sure that the template is rendered properly. Now, TemplateEngine needs a resolver to render a template. Inspecting the source code I see that there's a default StringTemplateResolver if there's nothing else configured. The resolver just treats the first argument of process as a template string (instead of a template name which is what I want). Naturally then, this template resolver is used in my integration test which is set up like this:
#SpringJUnitConfig(classes = [SpringTemplateEngine::class])
class TemplateServiceIT #Autowired constructor(
springTemplateEngine: SpringTemplateEngine,
) {
private val templateService = TemplateService(springTemplateEngine)
}
This code "works" in the way that all the dependencies are correctly set up and I can create my TemplateService instance. However, SpringTemplateEngine is configured differently in this test than in the context of a real Spring Boot app because there an extra autoconfiguration is applied which sets up SpringResourceTemplateResolver which correctly treats templateName as a template name and not a template string.
Now to my question. I want to set up my test context in such a way that:
Spring injects auto-configured SpringTemplateEngine (with SpringResourceTemplateResolver)
I don't have to start the whole Spring Boot app with SpringBootTest

The correct way is to use #ImportAutoConfiguration:
#SpringJUnitConfig(TemplateService::class)
// This auto-configures SpringTemplateEngine
#ImportAutoConfiguration(ThymeleafAutoConfiguration::class)
class TemplateServiceIT #Autowired constructor(private val templateService: TemplateService) {
...tests
}

Related

Test #EventListener with custom events Spring Kotlin

I have created a set of custom events for my application
sealed class myEvent(open val id: Int) {
data class myBigEvent(override val id : Int) : myEvent(id)
data class myIntermediateEvent(override val id: Int): myEvent(id)
}
I have a service that has a method for listening my custom events
#Service
class MailService(
private val otherService: OtherService
) {
#EventListener(myEvent.myBigEvent::class, myEvent.myIntermediateEvent::class)
#Async
fun handleProcessEvent(event: myEvent) {
if (event.id != 10 && otherService.hasCompleted) {
sendMail(event)
}
}
The interesting point is that IntellIj annotates with the eventlistener icon next to the method defintion. When clicking on it I get redirected to all my invocations of publisher.publishEvent(myBigEvent) or publisher.publishEvent(myIntermediateEvent)
The issue is when I try to test it. I have the following setup
#TestExecutionListeners
class myEventTest {
#Autowired
private lateinit var publisher: ApplicationEventPublisher
#Mock
private lateinit var mailService: MailService
#BeforeClass
fun start() {
publisher = ApplicationEventPublisher {}
MockitoAnnotations.initMocks(this)
}
#Test
fun `should receive cmlaProcessEvent published event`() {
val cmlaProcessEvent = JobProcessEvent.CmlaProcessSimulationProcessEvent(mlaSimulationRun)
this.publisher.publishEvent(cmlaProcessEvent)
verify(mailService, times(1)).handleProcessEvent(cmlaProcessEvent)
}
}
I get 'Wanted but not invoked' ... 'Actually, there were zero interactions with this mock.'
I think my issue is with the ApplicationEventPublisher that is not sending the events to my MailService under this test context ?
Remove #TestExecutionListeners, since the default listeners should suffice.
If you're using Spring Boot, you should use #MockBean instead of #Mock.
In any case, you have to actually instruct JUnit to use Spring's testing support.
I assume that you are using JUnit 4, since I see #BeforeClass in the example. So I'm basing the following on that assumption (and using Java syntax instead of Kotlin).
You can instruct JUnit 4 to use Spring's testing support via #RunWith(SpringRunner.class). Note, however, that you'll also need to specify where your ApplicationContext configuration is.
For Spring Framework, you can do that with #ContextConfiguration. For Spring Boot, you'll likely want to use #SpringBootTest. Consult the documentation for testing support in Spring Framework and Spring Boot for details.
As a side note, you can also test ApplicationEvent publication without using a mock by using Spring Framework's built-in testing support for application events.

How to mock constructor injected #Value property in Spring Boot Unit Testing

I have a service as below with constructor dependency injection of a service and a configuration property.
#Service
public class MyService {
private OtherService service;
private final SomeClass c;
#Autowired
public MyService(
OtherService service,
#Value("${app.some-property}") String someProperty) {
this.service = service;
final String key = service.getKey();
SomeClient client = new SomeClient(key, someProperty);
c = new SomeClass(client);
}
}
How should we inject/mock value for property ${app.some-property} in unit test case using Mockito?
What also works is using the 'ReflectionTestUtils' provided by spring to set the values within the mock:
ReflectionTestUtils.setField(target, "name", value);
in this case target would be the mocked class in your unit test, e.g. annotated by #Mock and the static call above could be done in a function thats executed before all or before each test.
Spring ReflectionUtils
You can but you should not, because #Value is a spring annotation, and to get values assigned into it you should use the Spring provided Test mechanism.
Instead of using #RunsWith(MockitoRunner.class) start using #ExtendsWith(SpringExtension.class)
And create a test property file in your src/test/resources directory with the needed property, and then your test can get that property value assigned automatically

How to test a configuration class in Spring Boot

I have the following Spring Boot configuration class so that an index is created in our MongoDB:
#Configuration
#DependsOn("mongoTemplate")
#Profile({ "!test" })
public class CollectionsConfig {
private final MongoTemplate mongoTemplate;
#Autowired
CollectionsConfig(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
#PostConstruct
public void initIndexes() {
mongoTemplate.indexOps("db-name"); // collection name string or .class
.ensureIndex(
new Index().on("fechaAlta", Sort.Direction.ASC).expire(15552000)
);
}
}
How could I test this since this class doesn't create any bean or anything like that? I have seen examples of configuration classes where they use the application context to test if a bean is created, but I am not creating any bean here so I don't know what to do to run this code.
Help please.
I'd assume a meaningful way to test this configuration would be to verify the side effects of the #PostConstruct-annotated method.
For example, you could have a #SpringBootTest loading this configuration, then have your test verify that on your embedded version of Mongo DB the index on fechaAlta has been successfully created.
An alternative would be writing a test where you manually instantiate CollectionsConfig, pass it a mocked MongoTemplate instance, manually invoke the #PostConstruct-annotated method and ultimately verify the expected interactions with the MongoTemplate are satisfied.

Adding legacy singleton to Spring ApplicationContext for Injection

I am trying to create a lightweight web service around a legacy java library to expose it as a web service using Spring Boot. I am new to Spring, while I have a lot of java experiance writing libraries all my web service experiance is in ASP.NET.
I can instantiate an instance of my library object but I can't figure out how to then have that object be injected into my controllers via #Autowired when the application is spun up.
This is my main application:
#SpringBootApplication
public class ResolverWebServiceApplication {
private static ArgumentParser newArgumentParser() {
ArgumentParser parser = ArgumentParsers.newFor("Resolver").build();
// configuring the parser
return parser;
}
public static void main(String[] args) throws ArgumentParserException {
ArgumentParser parser = newArgumentParser();
Namespace ns = parser.parseArgs(args);
ResolverOptions options = new ResolverOptions.Builder(ns)
.build();
ResolverContext context = new ResolverContext(options);
// ^^^ I need to get this injected into my controllers ^^^
SpringApplication.run(ResolverWebServiceApplication.class, args);
}
}
And then a simple controller which needs the class injected:
#RestController
public class VersionController {
#Autowired
private ResolverContext context; // And here the instance needs to be injected.
#GetMapping(path = "/version", produces = MediaType.APPLICATION_JSON_VALUE)
public long version() {
return context.getResolver().getVersionAsLong();
}
}
I could make the context a singleton which the controllers just refer to but I want to be able to test my controllers by mocking the context. There is also obviously a lot of validation and error handeling that needs to be added.
I can't have it be a Bean since I only want to instantiate one for my entire application.
The closest question I have found is this one: Registering an instance as 'singleton' bean at application startup. But I can't put the options in the configuration files. The application might be spun up in a container or on a users machine and requires the ability to accept arguments to initialize the library class. It would be a real usability degradation if someone had to manually edit the application config for these options.
You need to tell spring to consider the required classes from your lib when initializing the application context i.e Configure and let spring know how to create a bean and then let spring handle dependency injection for you.
First of all, add required jar that you have in your build file, say pom.xml for maven, in your current project. Idea is to have it on your classpath when you build the project.
As you said it is legacy lib and I am assuming it is not a spring bean, then
In your configuration class, return it as a bean, using #Bean annotaion.
#Configuration
public class YourConfigurationClass {
#Bean
SomeBean returnSomeBeanFromLegacyLib() {
return new SomeClassFromLib();
}
Once you return this bean from your config, it should be available to Spring Context for dependency injection whereever you #Autowire the required dependency.

How to write the unit test case for the Classes which are annotated with #Configuration in Spring Boot application

I have a Configuration Class, which creates the bean for RedissonClient and also Creates the CacheManager. How to create the Unit Test case for this Configuration classes.
Can we write unit test case for #Configuration Class?
If we can, How we need to develop.
I prefer to write the test case in Spock Framework, with Groovy. If not, using Junit or Mockito Framework. How to write the unit test case for the Classes which are annotated with #Configuration in Spring Boot application
#Configuration
public class CacheConfiguration {
private static final String CONFIG= "Configuration";
#Value("${redis.server.url}")
private String redisUrl;
#Value("${redis.server.password}")
private String password;
#Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer().setAddress(redisUrl).setPassword(password);
RedissonClient client = Redisson.create(config);
return client;
}
#Bean
public CacheManager redissonCacheManager(RedissonClient redissonClient) {
Map<String, CacheConfig> config = new HashMap<String, CacheConfig>();
config.put(CONFIG, new CacheConfig(24*60*1000, 12*60*1000));
return new RedissonSpringCacheManager(redissonClient, config);
}
}
I think you should realize that classes annotated with #Configuration are not really java classes, or at least you should not treat them like this. I know it sound controversial, I'll explain...
So historically spring used XML configurations to declare beans.
In spring 2.5 I guess, they've introduced an annotation based method where you put annotations #Component/#Service on classes, put #Autowired wherever you want spring to inject dependencies, then spring starts, scans the classpath, detects the beans and starts the application context with these beans.
Then Spring 3.0 has introduced a Java Configuration way of doing spring related configurations:#Configuration / #Bean in a special class.
So you should view these configuration classes as a "substitution" to the methods I've described before
Now let me ask, do you think you should test and XML bean configuration on its own? Probably not...
Do you think you should test that class has annotation #Component on it and all the necessary dependencies are autowired (with reflection or whatever)? Probably not.
So why you want to test the Java Config classes?
Here is another argument that you might find interesting
I've said that these classes are solely for spring to resolve the beans and it internally runs them. But spring doesn't just "run" them - it creates run-time wrapper for them to overcome some technicalities. Here is an example of one such thing:
Lest assume we have three beans: A,B,C such as B and C depend on A.
class A {}
class B {
private A a;
public B(A a) {this.a = a;}
}
class C {
private A a;
public C(A a) {this.a = a;}
}
All beans are expected to be singletons so we define the configuration like this:
#Configuration
public class MyConfig {
#Bean
public A a() { return new A(); }
#Bean
public B b() { return new B(a()); }
public C c() {return new C(a()); }
}
Note that we call a() in definitions of B and C
Now, let me ask you a question: If its a "regular" java code how two different invocations of method a() (in B's and C's constructor) respectively are supposed to return the same instance of A?
So spring indeed uses a lot of sophisticated code and this is what actually runs in runtime, not your class as is, but the transformed version of it and you never know what are those transformations exactly since its and internal spring thing. But is so what is the point of testing it as it is?
I believe there are more arguments like this, but the point is clear - don't test the Configuration classes on their own.
Instead you can use an integration test that will run the spring container and load all the classes required in the configuration. However, in this case you'll probably want to mock some classes (by using #MockBean for example) so it won't be a 100% accurate test.
In terms of code coverage - IMO you should exclude these classes from coverage altogether
After some research and found that, we can run the embedded redis server and we can check whether we able to connect to redis server by spin up the application. I don't if this is correct or not. But by doing so, it really takes time to complete it, took around 20 seconds. Used following dependency to test this // https://mvnrepository.com/artifact/it.ozimov/embedded-redis
testCompile group: 'it.ozimov', name: 'embedded-redis', version: '0.7.2'
#SpringBootTest(classes = [TestApp])
class CacheConfigurationSpec extends Specification {
#Shared
RedisServer redisServer;
def setupSpec() {
redisServer = RedisServer.builder()
.port(6379)
.setting("bind 127.0.0.1")
.setting("maxmemory 128M")
.build()
redisServer.start()
}
def cleanupSpec() {
if (redisServer != null) {
redisServer.stop()
}
}
#Autowired
private RedissonClient redissonClient;
def "load all contexts"() {
}
}
#SpringBootApplication
class TestApp {
static void main(String[] args){
SpringApplication.run(TestApp.class, args)
}
}

Resources