Spring Cucumber ActiveProfiles annotation not working with CucumberContext - spring-boot

I'm working on a project where we have a component which consists:
core
connector to external system 1
connector to external system 2
The connectors are mutually exlusive (if connector1 is active, connector2 is always inactive and vice versa). The core and a single connector are autowired on startup of the ApplicationContext. Which connector is instantiated is based on a value in the spring application properties.
We're writing integration tests using spring-cucumber (v6.2.2). For each external system, I want to run a set of cucumber tests. I've created 2 testsets using annotations on the cucumber scenario's which allows me to seperate the tests for connector1 and connector2.
The problem I'm having is that I need both testsets to run with a different spring profile, so I can use a different configuration. I can't find how to do this.
Current implementation (with a single profile):
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>6.2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>6.2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<version>6.2.2</version>
<scope>test</scope>
</dependency>
CucumberConnector1IT.java
package omitted.for.company.rules;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;
#RunWith(Cucumber.class)
#CucumberOptions(
features = { "classpath:feature/" },
glue = { "omitted.for.company.rules.cucumber.step" },
plugin = { "pretty", "json:target/cucumber-report/cucumber.json",
"html:target/cucumber-report/cucumber.html" },
tags = "#Connector1 and not #ignore" // tags make sure only applicable tests are run
)
public class CucumberConnector1IT {
}
CucumberConnector2IT.java
package omitted.for.company.rules;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;
#RunWith(Cucumber.class)
#CucumberOptions(
features = { "classpath:feature/" },
glue = { "omitted.for.company.rules.cucumber.step" },
plugin = { "pretty", "json:target/cucumber-report/cucumber.json",
"html:target/cucumber-report/cucumber.html" },
tags = "#Connector2 and not #ignore" // tags make sure only applicable tests are run
)
public class CucumberConnector2IT {
}
StepInitializer.java
package omitted.for.company.rules.steps;
import io.cucumber.java.Before;
import io.cucumber.spring.CucumberContextConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;
#SpringBootTest
#ActiveProfiles("test") // I can only get this to work if the #ActiveProfiles and #CucumberContextConfiguration annotations are on the same class
#CucumberContextConfiguration
public class StepInitializer {
// mock some beans to use in cucumber test
#MockBean
private EventProducer eventProducer;
#MockBean
private RestInterface restInterface;
#Before
public void setup() {
}
}
So far everything works. But what I need now is to put the #ActiveProfiles() annotation on a different class than the #CucumberContextConfiguration. If I can do this then I can annotate the correct step classes with the required profiles.
Problem is that I don't understand the spring annotations well enough to know which ones I can move and which ones I cannot. I found this example of exactly what I'm trying to do (spring-cucumber-profiles repo, notice the location of the #ActiveProfiles annotation here) . Unfortunately, it uses an older version of cucumber-spring (v5.6.0). That version doesn't yet have the #CucumberContextConfiguration annotation and does some magic with the spring context according to the documentation (Release notes of cucumber-spring). I tried to checkout the example repo and upgrade it to v6.2.2 but couldn't get it working with the new version.
If anyone spots what I'm doing wrong in my own examples, or has the possibility to get the example repo working with version 6.2.2 of cucumber-spring that would be much appreciated.
Thanks in advance! :)

I've solved the issue by separating the packages a bit, and creating separate StepInitializer classes for both testsets.
Current setup:
Test runner:
package omitted.for.company.rules;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;
#RunWith(Cucumber.class)
#CucumberOptions(
features = { "classpath:feature/" },
extraGlue = { "omitted.for.company.rules.cucumber.step.common" }, // used extraGlue instead of glue
plugin = { "pretty", "json:target/cucumber-report/cucumber.json",
"html:target/cucumber-report/cucumber.html" },
tags = "#Connector1 and not #ignore" // tags make sure only applicable tests are run
)
public class CucumberConnector1IT {
}
Context Configuration:
package omitted.for.company.rules.steps;
import io.cucumber.java.Before;
import io.cucumber.spring.CucumberContextConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;
#SpringBootTest
#ActiveProfiles({ "test", "test-connector1" })
#CucumberContextConfiguration
public class Connector1StepInitializer {
// mock some beans to use in cucumber test
#MockBean
private EventProducer eventProducer;
#MockBean
private RestInterface restInterface;
#Autowired
private ApplicationContext applicationContext;
#Autowired
private Environment environment;
#Before
public void setup() {
assertThat(applicationContext).isNotNull();
assertThat(environment.getActiveProfiles()).containsOnly("test","test-connector1");
}
}
Both connectors/test runners have their own runner class and their own ContextConfiguration class.
It's very important that the classes containing the #CucumberContextConfiguration annotation are not in the shared glue package (as provided in the extraGlue property in the #CucumberOptions annotation).
Package structure looks like this:
├───common
│ └───step // Contains shared steps. This path should be in the 'extraGlue' field of the runner classes
├───connector1
│ │ CucumberConnector1IT.java // Runner 1
│ └───step
│ Connector1Steps.java // Specific steps
│ Connector1StepInitializer.java // has #ActiveProfiles and #CucumberContextConfiguration annotations, use to mock beans
└───connector2
│ CucumberConnector1IT.java // Runner 2
└───step
Connector2Steps.java // Specific steps
Connector2StepInitializer.java // has #ActiveProfiles and #CucumberContextConfiguration annotations, use to mock beans
This way I can still use different spring profiles :).

Related

Spring Data JDBC + Querydsl: error "JdbcRepositoryFactory does not support Querydsl"

I want to use Spring Data JDBC with QueryDSL support. According to Spring documentation (https://docs.spring.io/spring-data/jdbc/docs/current/reference/html/#core.extensions.querydsl) it is supported, but I couldn't make it working.
I use MariaDB as database and my version of SpringBoot is 2.6.0.
My dependencies in pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-sql-spring</artifactId>
</dependency>
Configuration:
#Configuration
#EnableJdbcRepositories
#EnableTransactionManagement
public class DBConfig {
}
My entity class:
package com.test.model.metadata;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
#Table
public class Action {
#Id
private long id;
private String name;
...
}
My repository class:
package com.test.repository;
import java.util.List;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import com.test.model.Action;
import com.querydsl.core.types.Predicate;
#Repository
public interface ActionRepository extends PagingAndSortingRepository<Action, Long>, QuerydslPredicateExecutor<Action> {
#Override
List<Action> findAll(Predicate predicate);
}
QueryDSL predicate usage:
QAction action = QAction.action;
Predicate predicate = action.name.like("%Accept%");
List<Action> actions = actionRepository.findAll(predicate);
Q-classes are generated properly by preprocessor, compilation suceeds but during application startup I am getting error:
org.springframework.data.repository.core.support.UnsupportedFragmentException: Repository com.atende.db.metadata.jdbcrepository.ActionRepository implements org.springframework.data.querydsl.QuerydslPredicateExecutor but JdbcRepositoryFactory does not support Querydsl!
What yet could be missing in my solution?
When I am using QueryDSL directly with JdbcTemplate it works:
QAction action = QAction.action;
SQLTemplates dialect = new MySQLTemplates();
Connection connection = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
SQLQuery<String> query = new SQLQuery<>(connection, dialect);
List<Tuple> actions = query.select(action.id, action.name)
.from(action)
.where(action.name.like("%Action%"))
.fetch();
I also tried to use infobip querydsl library (https://github.com/infobip/infobip-spring-data-querydsl) but got the same error.
This is a misinterpretation of the documentation (not your fault, it is structured in an unfortunate way).
You are looking at the preface which describes the general way to work with Spring Data modules.
This does not indicate that the module at hand (JDBC in this case) supports all the features mentioned.
Spring Data JDBC does not provide Querydsl support.
That means that the #EnableJdbcRepositories which activates spring-data-jdbc repository implementation does not provide Querydsl support.
If you have infobip-spring-data-jdbc-querydsl-boot-starter on classpath and remove #EnableJdbcRepositories it should work.
QuerydslJdbcPredicateExecutor is the fragment implementation.

How I can resolve this problem (Class<SpringJUnit4ClassRunner> cannot be resolved to a type)?

I realized a simple projetc using spring and maven, my project contains an interface and 3 classes and the pom.xml file
Interface : CompactDisc.java
3 classes :
SgtPeppers.java that implements the CompactDisc interface
CDPlayersConfig.java is an empty class and contains the annotations of automatic scan
CDPlayersTest.java is a test class to test if the spring container works.
My problem is with #RunWith(SpringJUnit4ClassRunner.class) and #ContextConfiguration(classes=CDPlayersConfig.class), Eclipse suggests this proposition for the first annotation Class cannot be resolved to a type and it doesn't understand the second annotation.
You find here the code :
CompactDisc.java
package soundsystem;
public interface CompactDisc {
void play();
}
SgtPeppers.java
package soundsystem;
import org.springframework.stereotype.Component;
#Component
public class SgtPeppers implements CompactDisc{
private String title = "Sgt. Pepper's Lonely Hearts Club Band";
private String artist = "The Beatles";
public void play() {
System.out.println("Playing " + title + "by"+ artist);
}
}
CDPlayersConfig.java
package soundsystem;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan
public class CDPlayersConfig {
}
CDPlayerTest.java
package soundsystem;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.jta.SpringJtaSynchronizationAdapter;
#RunWith(SpringJUnit4ClassRunner.class)//Here is my problem
#ContextConfiguration(classes=CDPlayersConfig.class)//Here is my problem
public class CDPlayerTest {
#Autowired
private CompactDisc cd;
#Test
public void cdShouldNotBeNull() {
assertNotNull(cd);
}
}
Dependencies im the pom file
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.0.5.RELEASE</version>
<scope>test</scope>
</dependency>
In Eclipse
Right Click on your Project
Select Maven
Select Update Project
Tick
Update project config from pom.xml
Refresh workspace
Clean projects
After doing that the required libraries should be available to Eclipse and you should be able to import the classes as required.
I can't see
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.ContextConfiguration;
in your import section. Also, maybe you should use SpringRunner.class instead of SpringJUnit4ClassRunner.class
Control+click on the class. Eclipse will open the file. Copy the package name. Create a new import statement and paste the package name + the class name. Problem solved.
If Eclipse does not open the file when you control+click, you don't have it on your class path or imported correctly.
You are missing the imports (import org.springframework...) for SpringJUnit4ClassRunner and ContextConfiguration. That's why they are not recognized.
You have to add dependency:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
And then
import org.springframework.test.context.ContextConfiguration;
It's working in my Gradle project after complete some steps.
1st: Add dependency in build.gradle file ->
testImplementation 'junit:junit:4.13.2'
implementation 'org.springframework:spring-test:4.0.0.RELEASE'
2nd: Clean Gradle project using IDE or using command prompt -> Open command prompt -> Go to project location c:\gradle_projects\test_gradle>gradlew clean build -> Enter
3rd: Refresh Gradle project -> Open command prompt -> Go to project location c:\gradle_projects\test_gradle>gradlew --refresh-dependencies -> Enter
4th: Go to IDE -> right-click on project name -> Gradle -> Refresh Gradle Project
-> finally download jar files and import class

Spring Data repository and native query with Geometry return type

I'm creating a project that uses some spatial queries.
I use Spring boot with spring data repositories and PostgreSQL with PostGIS extension as database.
I created this repository:
import com.vividsolutions.jts.geom.Geometry;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
#Repository
public interface AreaRepository extends CrudRepository<Area, Long> {
/*
extra queries for Area here
*/
#Query(value="select st_intersection(" +
":base_layer ," +
":filter_layer" +
")", nativeQuery = true)
Geometry geometryIntersectGeometry(#Param("base_layer") Geometry baseGeometry,#Param("filter_layer") Geometry filterGeometry);
}
It contains some queries for the Area entity. I also want to use some PostGIS functions to do some calculations, so I created geometryIntersectGeometry to call the st_intersection function from PostGis, this should return a geometry.
I set the hibernate dialect to PostGIS in the settings:
spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/test_db
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.properties.hibernate.dialect = org.hibernate.spatial.dialect.postgis.PostgisDialect
And I have the dependencies for hibernate spatial:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-spatial</artifactId>
<version>${hibernate.version}</version>
</dependency>
...
Calling the geometryIntersectGeometry function results in an error:
No Dialect mapping for JDBC type: 1111; nested exception is org.hibernate.MappingException: No Dialect mapping for JDBC type: 1111,{}
How do I tell JPA/Spring Data to map the geometry (PostGIS type) response to a Geometry(com.vividsolutions.jts.geom.Geometry) object?
Managed to fix it by writing a custom implementation of the repository and register the type (thx Simon Martinelli)
repository:
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
#Repository
public interface AreaRepository extends CrudRepository<Area, Long>, AreaGisRepository {
/*
extra queries for Area here
*/
}
interface:
import com.vividsolutions.jts.geom.Geometry;
public interface AreaGisRepository {
Geometry geometryIntersectGeometry(Geometry baseGeometry, Geometry filterGeometry);
}
and implementation:
import com.vividsolutions.jts.geom.Geometry;
import org.hibernate.spatial.JTSGeometryType;
import org.hibernate.spatial.dialect.postgis.PGGeometryTypeDescriptor;
import org.springframework.beans.factory.annotation.Autowired;
import javax.persistence.EntityManager;
public class AreaGisRepositoryImpl implements AreaGisRepository {
private EntityManager entityManager;
#Autowired
public AreaGisRepositoryImpl(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
public Geometry geometryIntersectGeometry(Geometry baseGeometry, Geometry filterGeometry) {
return (Geometry) entityManager
.createNativeQuery(
"select st_intersection(:base_layer , :filter_layer) as geom")
.setParameter("base_layer", baseGeometry)
.setParameter("filter_layer", filterGeometry)
.unwrap(org.hibernate.query.NativeQuery.class)
.addScalar("geom", new JTSGeometryType(PGGeometryTypeDescriptor.INSTANCE))
.getSingleResult();
}
}
It works perfect, but I now have a hard coded dependency on Postgis (not likely we will use something else, but ...)
Have you added Hibernate Spatial?
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-spatial</artifactId>
<version>${hibernate.version}</version>
</dependency>
This has support for GIS data: http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#spatial
I think there is a simpler way to get this done. Hibernate Spatial registers a number of spatial functions for use in HQL/JQL. So the following should work
#Query(value="select intersection(" +
":base_layer ," +
":filter_layer" +
")")
Geometry geometryIntersectGeometry(#Param("base_layer") Geometry baseGeometry,#Param("filter_layer") Geometry filterGeometry);
See the documentation for a list of functions available in the Spatial Dialects.

WebDrivermanager is not opening the browser

I am not able to open both IE and Chrome browsers using WebDriverManager dependency when i run as Maven test. I see that no error is thrown in console and test execution is inprogress even after several minutes.
I have a println statement before the opening the browser which gets printed in the console. Can someone please help me on this?where am I going wrong ?
I am using Spring Test Suite 3.3.0(instead of Eclipse), Java 1.8, Chrome Version - 67.0.3396.99, IE version - 11.0.60
Dependency in pom.xml
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>2.2.3</version>
</dependency>
</dependencies>
Java Class code
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.testng.annotations.Test;
public class SampleTest {
WebDriver driver;
#Test(priority = 2)
public void TC01()
{
System.out.println("Inside TC1");
WebDriverManager.iedriver().setup();
driver = new InternetExplorerDriver();
driver.get("https://www.google.com/");
}
#Test(priority = 1)
public void TC02()
{
System.out.println("Inside TC2");
}
}
Console
You can find a running example of Internet Explorer and WebDriverManager here. Moreover, take a look to the required configuration in Internet Explorer according to the Selenium doc.

TilesConfigurer is deprecated ?? How I use tile2.2 in spring MVC 3.1?

I try to integrate Spring MVC 3.1 with Apache Tile2.2 but I found this error
Error creating bean with name 'tilesConfigurer' defined in ServletContext resource [/WEB-INF/tiles-context.xml]: Invocation of init method failed
so I search it in google and I found in Apache Tile2 structure had change or deprecated but spring mvc 3.1 still use old structure. (Someone said We must modify the class or etc.)
These are my lib I used:
tiles-api-2.2.2.jar
tiles-core-2.2.2.jar
tiles-el-2.2.2.jar
tiles-jsp-2.2.2.jar
tiles-servlet-2.2.2.jar
tiles-template-2.2.2.jar
adn spring mvc
org.springframework.aop-3.1.0.M1.jar
org.springframework.asm-3.1.0.M1.jar
org.springframework.aspects-3.1.0.M1.jar
org.springframework.beans-3.1.0.M1.jar
org.springframework.context.support-3.1.0.M1.jar
org.springframework.context-3.1.0.M1.jar
org.springframework.core-3.1.0.M1.jar
org.springframework.expression-3.1.0.M1.jar
org.springframework.instrument.tomcat-3.1.0.M1.jar
org.springframework.instrument-3.1.0.M1.jar
org.springframework.jdbc-3.1.0.M1.jar
org.springframework.jms-3.1.0.M1.jar
org.springframework.orm-3.1.0.M1.jar
org.springframework.oxm-3.1.0.M1.jar
org.springframework.test-3.1.0.M1.jar
org.springframework.transaction-3.1.0.M1.jar
org.springframework.web.portlet-3.1.0.M1.jar
org.springframework.web.servlet-3.1.0.M1.jar
org.springframework.web.struts-3.1.0.M1.jar
org.springframework.web-3.1.0.M1.jar
Anyone know how I fix this ? It will be useful to me.
If you import from org.springframework.web.servlet.view.tiles3 package you will not get any depricated issue but if you import from org.springframework.web.servlet.view.tiles2 it is depreciated. I solved the issue by changing imports.
Configuration class
package mum.waa;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.view.tiles3.TilesConfigurer;
import org.springframework.web.servlet.view.tiles3.TilesView;
import org.springframework.web.servlet.view.tiles3.TilesViewResolver;
#Configuration
public class TilesConfiguration{
public TilesConfigurer tilesConfigurer()
{
final TilesConfigurer configurer = new TilesConfigurer();
configurer.setDefinitions(new String[] { "WEB-INF/tiles.xml" });
configurer.setCheckRefresh(true);
return configurer;
}
#Bean
public TilesViewResolver tilesViewResolver() {
final TilesViewResolver resolver = new TilesViewResolver();
resolver.setViewClass(TilesView.class);
return resolver;
}
}
Dependencies
<!-- Apache Tiles -->
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-extras</artifactId>
<version>3.0.7</version>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-jsp</artifactId>
<version>3.0.7</version>
</dependency>
<!-- Apache Tiles -->
(Answered by the OP in a question edit. Converted to a community wiki answer. See Question with no answers, but issue solved in the comments (or extended in chat) )
The OP wrote:
solution:
This is not error by deprecated it's about jar file that didn't right version.
I solved by find right jar to my web application
this is my jar
commons-beanutils-1.8.3.jar
commons-beanutils-bean-collections-1.8.3.jar
commons-beanutils-core-1.8.3.jar
commons-digester-2.1.jar
jcl-over-slf4j-1.6.3.jar
slf4j-api-1.6.3.jar
slf4j-log4j12-1.6.3.jar
tiles-api-2.2.2.jar
tiles-core-2.2.2.jar
tiles-jsp-2.2.2.jar
tiles-servlet-2.2.2.jar
For some people get lost like me, Mart

Resources