Cucumber Test a Spring Boot Application - java

Does anyone know where I can find a sample application where Cucumber is used to test a Spring Boot application through Gradle? I can run the tests fine starting the server on the cmd line and using my IDE, but I need to be able to run them all programmatically on the CI server. I saw the answer on here but that solution did not work for me, most likely because I have multiple step def files.
Here is my setup
build.grade (Mentioned in the other question)
testCompile ("org.springframework.boot:spring-boot-starter-test",
...
"info.cukes:cucumber-spring:${cucumberVersion}")
CucumberTest.java
#RunWith(Cucumber.class)
#CucumberOptions(format = "pretty", features = "src/test/resources")
public class CucumberTest{
}
AbstractSpringTest.java (Extended by all the StepDef files)
#SpringApplicationConfiguration(classes = Application.class)
#RunWith(SpringJUnit4ClassRunner.class)
#Ignore
public abstract class AbstractSpringTest
{ ... }
It's not doing the correct thing on start up because its 1. trying to initialize my step def files and 2. My application is not started and the cucumber tests cannot make a connection.
Thanks.
EDIT: Or if someone can tell me how to start and stop the application using gradle, that would be acceptable as well.

I have solved the issue with some help from this question.
Here is the repository with the answer:
https://github.com/jakehschwartz/spring-boot-cucumber-example
In short, the AbstractSpringTest class needs to have the following annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = DemoApplication.class, loader = SpringApplicationContextLoader.class)
#WebAppConfiguration
#IntegrationTest

I had a similar symptom, my cucumber wouldn't start up the Spring context...
Turns out I had missed (one of) the following dependencies:
build.gradle
testCompile "info.cukes:cucumber-junit:1.2.4"
testCompile "info.cukes:cucumber-java:1.2.4"
testCompile "info.cukes:cucumber-spring:1.2.4"
StepDefs.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(
loader = SpringApplicationContextLoader.class,
classes = Application.class
)
#WebIntegrationTest(randomPort = true)
public class StepDefs {
#Value("${local.server.port}")
int port;
}
Update: SpringBoot 1.5.1
#ContextConfiguration(
loader = SpringBootContextLoader.class,
classes = Application.class
)

Further to #jakehschwartz, if you want the web app to start on a random available port, AbstractSpringTest needs:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class, loader = SpringApplicationContextLoader.class)
#WebIntegrationTest({"server.port=0"})
public abstract class AbstractSpringTest {
#Value("${local.server.port}")
protected int serverPort;
...}

I did something like this to get Spring to work with JUnit parameterized tests. It should be the same concept for Cucumber, but I haven't tried it. I was using XML configuration, so that might make a difference.
RunWithSpringJUnit4
public abstract class RunWithSpringJUnit4 {
private TestContextManager testContextManager;
public RunWithSpringJUnit4() {
try {
this.testContextManager = new TestContextManager(getClass());
this.testContextManager.prepareTestInstance(this);
} catch (Exception e) {
e.printStackTrace();
}
}
}
CucumberTest
#RunWith(Cucumber.class)
#CucumberOptions(format = "pretty", features = "src/test/resources")
public class CucumberTest extends RunWithSpringJUnit4 {
}

First, you'll need to ensure that you have applied spring-boot in gradle. Invoke gradle build which will produce a runnable jar. Instead of having your manifest call for the Spring class as your main, have a wrapper that starts it in a thread, waits for it to settle down and runs Cucumber:
#RunWith(Cucumber.class)
public class LucasePsCucumberTest implements Runnable {
public static void main(String[] args) {
Thread t = new Thread(this);
t.start();
// wait for t
cucumber.api.cli.Main(null);
}
}

Related

How do i initiate Spring #Autowired instances when running a Cucumber test?

I'm able to initiate Spring when i'm debbuging StepDefinitions.java, but running the test from gradle produces null. Do I need an aditional glue?
Produces null: gradle cucumber
Produces null: running myFeature.features
Produces myService (working): running Stepdefinitions.java
I have tried the following code:
#ContextConfiguration(
classes = Application.class,
loader = SpringBootContextLoader.class)
#WebAppConfiguration
Current StepDefinitions:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = Application.class)
#WebAppConfiguration
public class StepDefinitions{
private static String roll;
#Autowired
MyService myService;
/** Reset all static values before running test cases */
#BeforeClass
public static void resetValues() {
roll = "";
}
//I use swedish, "Given"
#Givet("Jag har rollen {string}")
public void getRole(String roll) {
assertNotNull(roll);
this.roll = roll;
myService.useRole(roll);
}
}
gradle.build:
dependencies {
compile files('../libs/cucumber-spring-4.7.1.jar')
testCompile 'io.cucumber:cucumber-java:' + cucumberVersion
testCompile 'io.cucumber:cucumber-junit:' + cucumberVersion
...
}
task cucumber() {
dependsOn assemble, compileTestJava
doLast {
javaexec {
main = "cucumber.api.cli.Main"
classpath = configurations.cucumberRuntime +
sourceSets.main.output + sourceSets.test.output
args = ['--plugin', 'pretty', '--glue', 'steps/rufs',
'src/test/resources/features', '--tags','#rufs']
}
}
}
You are not getting JUnit involved anywhere when running from Gradle. #RunWith is used by JUnit, and this in turn is what prompts Spring to get involved. When Cucumber is running as your suite, it's ignoring those annotations because it doesn't understand them.
You'll need to use JUnit as your suite (i.e. not run cucumber.api.cli.Main). You then have a problem because you need to use two "runners": Cucumber and Spring.
The way around this is JUnit's "rules" for one of the runners. Spring has a Rule, but as far as I can see Cucumber does not. In this case, use the Cucumber runner:
#RunWith(Cucumber.class)
in combination with Spring's rules, as described here: How to run JUnit SpringJUnit4ClassRunner with Parametrized?

set #ActiveProfiles from test runner class

sorry for my bad english. I need to use different active profiles for testing. Can I specify them in the launching class?
Launch class:
#RunWith(Cucumber.class)
#CucumberOptions(
features = "src/test/features",
tags = "#extraPaymentWithCash",
glue = "ru.zoo.penguin.cucumber.steps",
snippets = SnippetType.CAMELCASE
)
//#ActiveProfiles({"h2", "h2_tm", "cash"}) This doesn't work, but i need to use these profiles
public class ExtraPaymentWithCashCucumberFeaturesTest {
}
Step class:
#ActiveProfiles({"h2", "h2_tm"}) //i need to get rid of it
#ContextConfiguration(classes = {OperationTerminalManagerConfig.class, MainConfig.class, OperationTerminalHandlerConfig.class, PaymentTerminalHandlerConfig.class, BankConfig.class,
ManagersConfig.class, DaoConfig.class, DatabaseConfig.class, PechkinsConfig.class, ShooterConfig.class, ProcessManagerConfig.class,
CancelCucumberTestConfiguration.class, PaymentTerminalManagerConfig.class, CancelTerminalManagerConfig.class, CancelTerminalHandlerConfig.class})
#Transactional
#Log4j2
public class CancelCucumberBaseTest {
#Autowired
protected SmsMessageManager smsMessageManager;
....
}

Spring boot Test fails saying, Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean

Test Class:-
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { WebsocketSourceConfiguration.class,
WebSocketSourceIntegrationTests.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
"websocket.path=/some_websocket_path", "websocket.allowedOrigins=*",
"spring.cloud.stream.default-binder=kafka" })
public class WebSocketSourceIntegrationTests {
private String port = "8080";
#Test
public void testWebSocketStreamSource() throws IOException, InterruptedException {
StandardWebSocketClient webSocketClient = new StandardWebSocketClient();
ClientWebSocketContainer clientWebSocketContainer = new ClientWebSocketContainer(webSocketClient,
"ws://localhost:" + port + "/some_websocket_path");
clientWebSocketContainer.start();
WebSocketSession session = clientWebSocketContainer.getSession(null);
session.sendMessage(new TextMessage("foo"));
System.out.println("Done****************************************************");
}
}
I have seen same issue here but nothing helped me. May I know what I'm missing ?
I have spring-boot-starter-tomcat as compile time dependency in the dependency Hierarchy.
This message says:
You need to configure at least 1 ServletWebServerFactory bean in the ApplicationContext, so if you already have spring-boot-starter-tomcat you need to either autoconfigure that bean or to do it manually.
So, in the test there are only 2 configuration classes to load the applicationContext, these are = { WebsocketSourceConfiguration.class, WebSocketSourceIntegrationTests.class }, then at least in one of these classes there should be a #Bean method returning an instance of the desired ServletWebServerFactory.
* SOLUTION *
Make sure to load all the beans within your configuration class
WebsocketSourceConfiguration {
#Bean
ServletWebServerFactory servletWebServerFactory(){
return new TomcatServletWebServerFactory();
}
}
OR also enable the AutoConfiguration to do a classpath scanning and auto-configuration of those beans.
#EnableAutoConfiguration
WebsocketSourceConfiguration
Can be done also at the Integration Test class.
#EnableAutoConfiguration
WebSocketSourceIntegrationTests
For more information check the SpringBootTest annotation documentation
https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/context/SpringBootTest.html
in 2.0.5.RELEASE i faced a similar issue when I had the following.
package radon;
..
#SpringBootApplication
public class Initializer {
public static void main(String[] args) {
SpringApplication.run(Config.class, args);
}
}
package radon.app.config;
#Configuration
#ComponentScan({ "radon.app" })
public class Config {
..
}
Changing the package of Initializer from radon to radon.app fixed the issue.
this is because spring is not able to load the properties file at runtime, i was using spring profiles and wasn't providing the (program or vm) argument at runtime( java -jar application.jar) , adding vm argument of profile resolved the issue for me.
java -jar -Dspring.profiles.active=dev application.jar
or using program argument
java -jar application.jar --spring.profiles.active=prod --spring.config.location=c:\config
For web applications, extends *SpringBootServletInitializer* in main class.
#SpringBootApplication
public class YourAppliationName extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(YourAppliationName.class, args);
}
}

Elasticsearch Spring boot integration test

I am looking for the way to add embedded elasticsearch to my spring boot integration test.
I looked at elastic search integration test but it does not work together with spring boot as both should uses different test runner.
I have a class test as below unfortunately it does not work with error:
java.lang.IllegalStateException: No context information for thread:
Thread[id=1, name=main, state=RUNNABLE, group=main]. Is this thread
running under a class
com.carrotsearch.randomizedtesting.RandomizedRunner runner context?
Add #RunWith(class
com.carrotsearch.randomizedtesting.RandomizedRunner.class) to your
test class. Make sure your code accesses random contexts within
#BeforeClass and #AfterClass boundary (for example, static test class
initializers are not permitted to access random contexts).
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = App.class)
#WebAppConfiguration
#IntegrationTest("server.port:0")
public class TestExample extends ElasticsearchIntegrationTest {
TestRestTemplate testRestTemplate = new TestRestTemplate();
#Value("${local.server.port}")
int port;
#Test
public void testOne(){
ResponseEntity<String> results = testRestTemplate.getForEntity(String.format("http://localhost:%d/client/1", port), String.class);
System.out.print(results);
}
}
Does anybody has some ideas how to make them run or what is alternatives ??
You can actually do what you need without any additional elasticsearch testing dependencies. The idea is basically to create an embedded node and then use the NodeClient to communicate with it.
For that, I created my own EmbeddedElasticsearchServer class which looks (more or less) like this:
public class EmbeddedElasticsearchServer implements InitializingBean {
public EmbeddedElasticsearchServer() {
ImmutableSettings.Builder elasticsearchSettings = ImmutableSettings.settingsBuilder()
.put("http.enabled", "false")
.put("path.data", "target/elasticsearch-data");
node = nodeBuilder()
.local(true)
.settings(elasticsearchSettings.build())
.node();
client = node.client();
}
#Override
public void afterPropertiesSet() throws Exception {
// Initialization stuff:
// - create required indices
// - define mappings
// - populate with test data
}
public Client getClient() {
return client;
}
}
Then, in spring configuration (let's call it integration-test-context.xml) I did this:
<bean id="embeddedElasticsearchServer"
class="com.example.EmbeddedElasticsearchServer" />
<bean id="elasticsearchClient"
class="org.elasticsearch.client.node.NodeClient"
factory-bean="embeddedElasticsearchServer"
factory-method="getClient" />
Then you can just autowire the client in your test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/integration-test-context.xml")
public abstract class AbstractElasticsearchIntegrationTest {
#Autowired
private Client elasticsearchClient;
// Your rests go here...
}

Infinitest fails but Maven works

I have Sprint Tool Suite 3.3 and the latest version of Infinitest installed. According to Infinitest, many of my classes have errors, but yet Maven builds just fine. Here is an example of some errors that the Problems tab in STS show:
InvalidDataAccessResourceUsageException (Table "ADDRESSTYPEREF" not found; SQL statement:...SeedAddressTypes.java /... line 34 Infinitest Test Failure
AssertionFailure (null id in com..model.base.Menu entry (don't flush the Session after an exception occurs)) in TestMenuBuilderIT.sortByTopMenu TestMenuBuilderIT.java
All the tests that show an error are integration that use Java Config for the EmbeddedDatabase:
public class TestMenuBuilderIT extends BaseItegration {
#Autowired
private MenuRepository menuRepository;
#Test
public void sortByTopMenu() {
List<Menu> testMenu = menuRepository.findAll(); <== offending line
...
}
And the configuration class:
#RunWith( SpringJUnit4ClassRunner.class )
#ContextConfiguration( loader = AnnotationConfigContextLoader.class, classes = { JpaConfig.class } )
public abstract class BaseItegration {
..
}
#Configuration
#EnableTransactionManagement
#ComponentScan( basePackages = { ...} )
#ImportResource( { "classpath:applicationContext.xml"} )
public class JpaConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType( EmbeddedDatabaseType.H2 ).setName( "testdb" )
.addScript( "classpath:embeddedDatabase.sql" ).build();
}
I don't see why this would work fine in Maven, but Infinitest has these DB related errors. I could not find any documentation saying that JavaConfig wouldn't work with Infinitest. I am able to right click and run each test "As A Unit Test" successfully.
Does anyone know what might cause this?

Categories