#Given or #When annotation of cucumber is throwing errors related spring boot configuration - spring-boot

I am writing cucumber BDD test cases .
All the dependencies of cucumber are included in pom.xml
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.trivago.rta</groupId>
<artifactId>cluecumber-report-plugin</artifactId>
<scope>test</scope>
<version>2.3.3</version>
</dependency>
In My step definition file , i have included SprintBootTest annotation
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class AccessProfileStepDefinition {
private final Logger log = LoggerFactory.getLogger(AccessProfileStepDefinition.class);
// Location of input payload, which will be used to send request to api server.
private static final String root_folder = "/testdata/bdd/json_for_accessprofile/";
private final static String create_useraccessprofile_payload = root_folder + "create_access_profile_req.json";
#Autowired
private AccessProfileHttpClient httpClient;
#Given("I am cbx system user")
public void i_am_cbx_system_user() {
// Write code here that turns the phrase above into concrete actions
throw new io.cucumber.java.PendingException();
}
I get error -
when i am run my test
mvn -DAccessProfileFeatureBDDTest clean test
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
If i comment #Given clause in my step definition file, then i do not get the error related to #SpringBootConfiguration
Other files . is below
package com.igtb.dcp.cbxaccessprofile.bdd;
import org.junit.runner.RunWith;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
#RunWith(Cucumber.class)
#CucumberOptions(features = "src/test/resources/features/accessprofile/", plugin = {
"json:target/cucumber-report/cucumber.json", "com.igtb.dcp.cbxaccessprofile.bdd.TestInitialization" })
public class AccessProfileFeatureBDDTest {
}
accessprofile.feature has the feature and is contained in src/test/resources/features/accessprofile/ folder

If i comment #Given clause in my step definition file, then i do not get the error related to #SpringBootConfiguration
If you don't have a step definition in the class annotated with the context configuration Cucumber will not detect any context configuration at all and fall back to a GenericApplicationContext.
java.lang.IllegalStateException: Unable to find a
#SpringBootConfiguration, you need to use #ContextConfiguration or
#SpringBootTest(classes=...) with your test
With this error Spring is telling you that your #SpringBootTest couldn't find any configuration to build the application context from.
You either have to explicitly reference a class annotated with #SpringBootconfiguration, or make sure your #SpringBootApplication annotated class is in the same package, or add a #ContextConfiguration annotation to the AccessProfileStepDefinition.

Related

Required: Definitive Annotations and pom dependencies to test Apache Camel with Spring Boot and Java Config

I'm going round in circles with this one.
Because it uses Spring Test and Java Config, I copied the simple FilterTest example from here, which worked OK as was.
But as soon as I added an #Autowired field, I got an UnsatisfiedDependencyException [Unsatisfied dependency expressed through field ... No qualifying bean of type ... available].
After fixing that I now get a NullPointerException on resultEndpoint.expectedBodiesReceived(expectedBody); in the first #Test method. When this happens, there is absolutely no output in the console, so evidently Spring hasn't even started.
Here's the slightly modified test class:
package org.apache.camel.spring.javaconfig.patterns;
import org.apache.camel.EndpointInject;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.spring.javaconfig.SingleRouteCamelConfiguration;
import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import c10y.camel.converter.QbInvoiceConverter;
/**
* Tests filtering using Spring Test and Java Config
* page: https://camel.apache.org/manual/latest/testing.html
* code: https://github.com/apache/camel/blob/master/core/camel-core/src/test/java/org/apache/camel/processor/FilterTest.java
*/
#CamelSpringBootTest
#ContextConfiguration
#DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
public class FilterTest {
#EndpointInject("mock:result")
protected MockEndpoint resultEndpoint;
#Produce("direct:start")
protected ProducerTemplate template;
#Autowired
private QbInvoiceConverter converter;
#Test
public void testSendMatchingMessage() throws Exception {
String expectedBody = "<matched/>";
resultEndpoint.expectedBodiesReceived(expectedBody);
template.sendBodyAndHeader(expectedBody, "foo", "bar");
resultEndpoint.assertIsSatisfied();
}
#Test
public void testSendNotMatchingMessage() throws Exception {
resultEndpoint.expectedMessageCount(0);
template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
resultEndpoint.assertIsSatisfied();
}
#Configuration
public static class ContextConfig extends SingleRouteCamelConfiguration {
#Bean
public RouteBuilder route() {
return new RouteBuilder() {
public void configure() {
from("direct:start")
.filter(header("foo")
.isEqualTo("bar"))
.to("mock:result")
;
}
};
}
}
}
I have tried to bring this up to date with the migration instructions here. Briefly, this states:
Migration steps linked to JUnit 5 support in Camel Test itself should have been applied first
Imports of org.apache.camel.test.spring.* should be replaced with org.apache.camel.test.spring.junit5.*
Usage of #RunWith(CamelSpringRunner.class) should be replaced with #CamelSpringTest
Usage of #BootstrapWith(CamelTestContextBootstrapper.class) should be replaced with #CamelSpringTest
Usage of #RunWith(CamelSpringBootRunner.class) should be replaced with #CamelSpringBootTest
The other part of my question relates to having the correct dependencies in pom.xml. I'm learning Camel from Camel in Action E2, so my pom has test dependencies from the book as well as others I have picked up on the way; so there may be clashes or incompatibilities in there:
<!-- from .../camel-in-action-2/chapter9/spring-boot-test-one-route/pom.xml -->
<!-- test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test</artifactId>
<scope>test</scope>
</dependency>
<!-- testing -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-spring</artifactId>
<scope>test</scope>
</dependency>
<!-- from elsewhere -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-spring-junit5</artifactId>
<scope>test</scope>
</dependency>
I'm using Apache Camel 3.4.0 LTS and Spring Boot 2.3.0.RELEASE.
As the title to this question states, I'd really like to have the definitive annotation and pom configuration to allow me to simply get on with creating test routes to unit test my Components, Converters, and so forth [without tearing my hair out!].
PS: What's the difference between org.junit.Test and org.junit.jupiter.api.Test annotations, and when should you use one over the other?

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.
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;
There is a nice example here, in the attached github code. I was able to use it as a starting point to test my freeMarker code: https://cleantestcode.wordpress.com/2014/06/01/unit-testing-freemarker-templates/

Cucumber Spring DI two Step classes loading Spring context

I'm using cucumber-spring to create my integration tests and use DI provided by Spring.
The way it's done: there is a separate project that defines all the necessary cucumber dependencies:
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>${cucumber.version}</version>
</dependency>
The project also has a base test class annotated with SpringBootTest (that allows to load application context):
#RunWith(SpringRunner.class)
#SpringBootTest(
classes = {SomeBeans.class}
)
public class BaseTestStateSteps {
I'm adding this project as a dependency in the pom of my project:
<dependency>
<groupId>com.externalproject</groupId>
<artifactId>spring-test-cucumber</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
Then I have my runner class:
#RunWith(Cucumber.class)
#CucumberOptions(
plugin = {"pretty", "junit:target/cucumber/cucumber.xml", "json:target/cucumber/cucumber.json",
"html:target/cucumber/html/"},
tags = {"#Smoke"},
features = {"src/test/resources/features"},
glue = {"com.externalproject.steps","com.mycompany.steps"}
)
public class RunTestIT {
}
And I also have my Step class:
public class MyTestSteps {
#Autowired
ClassToAutowire classToAutowire;
#Given("^I can do something$")
public void generateRandomPhoneNumber() {
classToAutowirer.doSomething();
}
So the issue I'm running:
- if I don't have my RunTestIT annotated with SpringBootTest or with ContextConfiguration - ClassToAutowire is null (it's not autowired);
- if I addone of those annotations I'm running into error:
cucumber.runtime.CucumberException: Glue class class com.externalproject.steps.BaseTestStateSteps and class com.mycompany.steps.MyTestSteps both attempt to configure the spring context. Please ensure only one glue class configures the spring context
I need the functionality of Steps from external project (so I don't have to copy/paste from there and get the latest feature updates), but I also need dependencies from my spring context to be autowired.
I read the article https://thepracticaldeveloper.com/2018/03/31/cucumber-tests-spring-boot-dependency-injection/
But it only deals with multiple step definitions inside the same project.
Do you have any ideas how I can solve the issue?

SpringBoot 2 + Junit5: null with #Value

I have an application with SpringBoot2 and Junit5, and now I'm trying to make a test. I have a this class called OrderService that looks like this:
#Component
public class OrderService {
#Value("#{'${food.requires.box}'.split(',')}")
private List<String> foodRequiresBox;
#Value("#{'${properties.prioritization}'.split(',')}")
private List<String> prioritizationProperties;
#Value("${further.distance}")
private Integer slotMeterRange;
#Value("${slot.meters.long}")
private Double slotMetersLong;
As you can see, the class has many #Value annotations that extracts values from application.properties file.
In the POM file I have these dependences:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>2.0.5.RELEASE</version>
</dependency>
Tn the test/resources folder I have the application.properties file with this information:
properties.prioritization:vip,food
food.requires.box:pizza,cake,flamingo
further.distance:2
slot.meters.long:0.5
The test file looks like this:
#ExtendWith(SpringExtension.class)
#TestPropertySource(locations="classpath:application.properties")
public class OrderServiceTest {
OrderService orderService;
#BeforeEach
void before(){
orderService = new OrderService();
}
#Test
void findAll() {
Order order = new Order().withDescription("2x Pizza with Salad\\n2x Kebab with Fries\\n1x Hot dog with Fries\\n2x Pizza with Fries");
assertTrue(orderService.orderHasFood.test(order));
}
}
But the test throws NullPointerException when it tries to use foodRequiresBox, so there is a problem to read the application.properties file.
Could you tell how can I read the application.properties file for the tests?
1st Solution
I would recommend to use Spring's internal annotation called #SpringJUnitConfig
This annotation is actually the same as #ExtendWith(SpringExtension.class) BUT you can configure your spring application contexts for your test in the same way you used to use #ContextConfiguration.
Or if you want a full Spring Boot Test you could just combine:
#SpringJUnitConfig
#SpringBootTest
public class OrderServiceTest {
...
}
2nd Solution
Another way is to not use Spring at all, but mock all the internal stuff with e.g. Mockito and write a plain simple Unit Test.
You could then set your normally via Spring injected annotated #Value fields via org.springframework.test.util.ReflectionTestUtils.
I'd recommend using org.springframework.test.util.ReflectionTestUtils (as indicated in #mrkernelpanic second solution) using the setField() method, in order to avoid initializing the full Spring context.
Here is a snippet:
// ReflexionTestUtils.setField(targetObject, "targetFieldName", valueToInject);
ReflexionTestUtils.setField(orderService, "foodRequiresBox", "pizza,cake,flamingo");

Spring Framework: Failed to load ApplicationContext

While running script in Spring framework it is throwing error message as "Failed to load ApplicationContext". I have my ApplicationContext.xml file under src/test/resources
And i was using
#RunWith(value = Parameterized.class)
public class CustomerTest extends FunctionLibrary { ... }
After some suggestions from forum i used
#RunWith(value = Parameterized.class)
#ContextConfiguration(locations = "file:src/test/resources/applicationContext.xml")
public class CustomerTest extends FunctionLibrary { ... }
Still i am getting the same error message. Please anyone tell me how to resolve this issue?
Thanks,
Sudhansu
There are several problems
1) use #RunWith(SpringJUnit4ClassRunner.class)
2) Your path sounds like you have a maven project. In a maven project, src/test/resources will be added to the classpath when the tests run. So use:
#ContextConfiguration(locations = "classpath:applicationContext.xml")
If you haven an other file in src/main/resources withe the same name, then I recommend to rename the one in src/test/resources to test-applicationContext.xml and then use #ContextConfiguration(locations = "classpath:test-applicationContext.xml"), just to reduce the the number of knot in your brain
Complete example:
package com.test.service;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classpath:test-ApplicationContext.xml)
public class WhateverTest {
...
}
pom excerpt:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.6.RELEASE</version>
</dependency>

Resources