Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing Freemarker templates in SpringBoot - unable to initialize freemarker configuration

we are using Freemarker for generating the HTML code for the emails our application is going to be sending.

Our usage and configuration is based off of https://github.com/hdineth/SpringBoot-freemaker-email-send Particularly:

package com.example.techmagister.sendingemail.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean;

import java.io.IOException;

@Configuration
public class FreemarkerConfig {

    @Bean(name="emailConfigBean")
    public FreeMarkerConfigurationFactoryBean getFreeMarkerConfiguration(ResourceLoader resourceLoader) {
        FreeMarkerConfigurationFactoryBean bean = new FreeMarkerConfigurationFactoryBean();
        bean.setTemplateLoaderPath("classpath:/templates/");
        return bean;
    }
}

However, there is absolutely no information or documentation anywhere, about how to run Unit Tests for this using JUnit 5.

When I added the relevant dependencies

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>${mockito.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <version>${mockito.version}</version>
            <scope>test</scope>
        </dependency>

versions:

        <junit.jupiter.version>5.3.1</junit.jupiter.version>
        <mockito.version>2.23.0</mockito.version>

And made a test class:

package com.example.techmagister.sendingemail;

import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.io.IOException;


@ExtendWith({SpringExtension.class, MockitoExtension.class})
@Import(com.example.techmagister.sendingemail.config.FreemarkerConfig.class)
public class EmailTestTest {
    private static final Logger LOGGER = LogManager.getLogger(EmailTestTest.class);

    @Autowired
    @Qualifier("emailConfigBean")
    private Configuration emailConfig;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void test() throws Exception {
        try {
            Template template = emailConfig.getTemplate("email.ftl");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

When I run that in debug mode, emailConfig is null. Why is that?

Their test example https://github.com/hdineth/SpringBoot-freemaker-email-send/blob/master/src/test/java/com/example/techmagister/sendingemail/SendingemailApplicationTests.java works if I add the same autowired property, but it is a full SprintBoot context that is slow to boot, and I need to test just template usage, without actually sending out the email.

In our actual code (which is large, multi module project), I have another error org.springframework.beans.factory.UnsatisfiedDependencyException caused by:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'freemarker.template.Configuration' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=emailConfigBean)}

But that is just for context, first I want to get it working in the simple, sample project then worry about getting it working in our complex one.

like image 256
Carmageddon Avatar asked Dec 06 '19 20:12

Carmageddon


1 Answers

You cannot autowire your emailConfigBean directly as a freemarker.template.Configuration FreeMarkerConfigurationFactoryBean is a factorybean. To get the Confuguration you need to call factorybean.getObject()

so instead of

    @Autowired
    @Qualifier("emailConfigBean")
    private Configuration emailConfig;

just autowire your factorybean FreeMarkerConfigurationFactoryBean and load your template with emailConfig.getObject().getTemplate("email.ftl")


    @Autowired
    @Qualifier("emailConfigBean")
    private FreeMarkerConfigurationFactoryBean emailConfig;

    @Test
    void testFreemarkerTemplate(){
        Assertions.assertNotNull(emailConfig);
        try {
            Template template =
                emailConfig
                    .getObject()               // <-- get the configuration
                    .getTemplate("email.ftl"); // <-- load the template
            Assertions.assertNotNull(template);
        } catch (Exception e) {
            e.printStackTrace();
        }

working test on github


On the other hand... In a Spring Boot application the Freemarker configuration can be simplified by using the spring-boot-starter-freemarker dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
    <version>1.5.6.RELEASE</version>
</dependency>

This starter for building MVC web applications using FreeMarker views adds the necessary auto-configuration. All you need to do is placing your template files in the resources/templates folder.

Then you just can autowire the freemarkerConfig (or use constructor injection):

    @Autowired
    private Configuration freemarkerConfig;
like image 73
Dirk Deyne Avatar answered Nov 15 '22 02:11

Dirk Deyne