Antonio Dragos
Antonio Dragos

Reputation: 2873

Integration test for single Spring Boot @Service class

I'm writing an integration test for this Spring Boot @Service bean

import org.springframework.stereotype.Service;
import org.thymeleaf.ITemplateEngine;
import org.thymeleaf.context.Context;

import java.util.Locale;
import java.util.Map;

@Service
public class ThymeLeafEmailTemplateService implements EmailTemplateService {

    private final ITemplateEngine springTemplateEngine;

    public ThymeLeafEmailTemplateService(ITemplateEngine springTemplateEngine) {
        this.springTemplateEngine = springTemplateEngine;
    }

    public String generateEmailBody(String template, Map<String, Object> variables) {
        Context context = new Context(Locale.getDefault(), variables);
        return springTemplateEngine.process(template, context);
    }
}

Currently, the test class is defined as shown below

@SpringBootTest
class ThymeLeafEmailTemplateServiceTests {


    @Autowired
    private EmailTemplateService service;

    @Test
    void generateTaskNotificationEmail() {
      var output = service.generateEmailBody("/template", Map.of());
      assertEquals("Expected Output", output);
    }
}

A problem with this approach is that it's very slow/inefficient because the entire application context is loaded, but I really only need the service being tested and its dependencies.

If I change the test class' annotations to

@SpringBootTest
@ContextConfiguration(classes = ThymeLeafEmailTemplateService.class)

the test fails, because the dependency ITemplateEngine springTemplateEngine does not exist. I could add this dependency to classes (the list of beans to create), but this seems like a very brittle approach.

Is there an efficient way to integration test a single @Service?

Note:

I know I could mock ITemplateEngine springTemplateEngine and write a unit test instead, but I want to test the template's actual output, so this approach won't work

Upvotes: 1

Views: 888

Answers (2)

OJVM
OJVM

Reputation: 1481

I had the exact requirement and solved it with the following config.

In my test case just call the components that are needed for the execution of the process, in this case, my service (CreaFormatoContrareciboServiceImpl) and the repository inside the service (ContrareciboRepository) and also a customized configuration for the database (RepositoryConfiguration) this is because the configuratión generated by jhipster it is not working correctly. found this here https://tanzu.vmware.com/

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ContrareciboRepository.class, CreaFormatoContrareciboServiceImpl.class, RepositoryConfiguration.class})
 class CreaFormatoContrareciboServiceTest {

    @Autowired
    private CreaFormatoContrareciboService creaFormatoContrareciboService;
    
    @Test
    void whenCodeIsFound() throws JRException {
        String codigo = "DEMO";
        
        JasperPrint jasperPrint = this.creaFormatoContrareciboService.obtener(codigo);
        
        assertNotNull(jasperPrint);
        
    }
}

Upvotes: 0

Stefan Bratanov
Stefan Bratanov

Reputation: 101

You can use

@WebMvcTest(ThymeLeafEmailTemplateService.class)

This will load only that bean in your application context along with any default Spring configuration beans.

Upvotes: 2

Related Questions