I am very trying the pact-jvm-provider-junit using IntelliJ IDEA as my IDE.
I'm testing out the provided ContractTest example:
import org.junit.BeforeClass;
import org.junit.Before;
import org.junit.ClassRule;
import au.com.dius.pact.provider.junit.State;
import au.com.dius.pact.provider.junit.Provider;
import au.com.dius.pact.provider.junit.target.TestTarget;
import au.com.dius.pact.provider.junit.target.Target;
import au.com.dius.pact.provider.junit.target.HttpTarget;
import au.com.dius.pact.provider.junit.TargetRequestFilter;
import org.apache.http.HttpRequest;
import au.com.dius.pact.provider.junit.PactRunner;
import au.com.dius.pact.provider.junit.loader.PactFolder;
import au.com.dius.pact.provider.junit.loader.PactUrl;
import au.com.dius.pact.provider.junit.VerificationReports;
import com.github.restdriver.clientdriver.ClientDriverRule;
import org.junit.runner.RunWith;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import static com.github.restdriver.clientdriver.RestClientDriver.giveEmptyResponse;
import static com.github.restdriver.clientdriver.RestClientDriver.onRequestTo;
#RunWith(PactRunner.class) // Say JUnit to run tests with custom Runner
#Provider("uicc_repository") // Set up name of tested provider
//#PactFolder("rs") // Point where to find pacts (See also section Pacts source in documentation)
#PactUrl(urls = {"file:///C:/IdeaProjects/src/pack-test-provider/resources/test_consumer-test_provider.json"}) // Point where to find pacts (See also section Pacts source in documentation)
#VerificationReports(value = {"markdown","json"}, reportDir = "C:/IdeaProjects/src/pack-test-provider/resources")
public class ContractTest {
// NOTE: this is just an example of embedded service that listens to requests, you should start here real service
#ClassRule //Rule will be applied once: before/after whole contract test suite
public static final ClientDriverRule embeddedService = new ClientDriverRule(6060);
private static final Logger LOGGER = LoggerFactory.getLogger(ContractTest.class);
#BeforeClass //Method will be run once: before whole contract test suite
public static void setUpService() {
//Run DB, create schema
//Run service
//...
}
#Before //Method will be run before each test of interaction
public void before() {
// Rest data
// Mock dependent service responses
// ...
embeddedService.addExpectation(
onRequestTo("/data"), giveEmptyResponse()
);
}
#TestTarget // Annotation denotes Target that will be used for tests
public final Target target = new HttpTarget(6060); // Out-of-the-box implementation of Target (for more information take a look at Test Target section)
#State("default") // Method will be run before testing interactions that require "default" or "no-data" state
public void toDefaultState() {
// Prepare service before interaction that require "default" state
// ...
System.out.println("Now service in default state");
LOGGER.info("Now service in default state");
}
}
But when I try to run the test (Run -> Run ContractTest), it's like the test is not ran at all:
Mar 15, 2017 3:24:00 PM org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry loadTestEngines
INFO: Discovered TestEngines with IDs: [junit-jupiter, junit-vintage]
Mar 15, 2017 3:24:01 PM org.junit.vintage.engine.execution.TestRun lookupTestDescriptor
WARNING: Runner au.com.dius.pact.provider.junit.PactRunner on class rs.ContractTest reported event for unknown Description: rs.ContractTest. It will be ignored.
Process finished with exit code 0
enter image description here
I am not sure if it's an issue with how I am running the test in IntelliJ or I am missing something in the pact-jvm-provider "runner".
Appreciate any help on this topic.
Thanks!
Related
I am trying to write integration test for neo4j using spring boot. I am using test container. Can anyone help me how to load database dump file to testcontainer?
here's one way to do this (using current Neo4j 5.3). I have a dump file called neo4j.dump created from a local instance and I use withCopyFileToContainer to copy it to the container before it starts.
As I use the community edition in this example, there is no online backup/restore, but only dump/load. So therefor I have to change the startup command. The load needs to happen before Neo4j starts. I can't stop Neo4j in the container because it would stop the container.
Therefor I create a small shell script that executes the desired other command and than delegates to the original entry point.
This script is transferred with file mode 0100555, corresponding to r-xr-xr-x so that it is executable.
Finally, the container is started with the above script as command.
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.images.builder.Transferable;
import org.testcontainers.utility.MountableFile;
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class LoadDumpTest {
Neo4jContainer<?> neo4j;
Driver driver;
#BeforeAll
void initNeo4j() {
neo4j = new Neo4jContainer<>("neo4j:5.3.0")
.withCopyFileToContainer(MountableFile.forClasspathResource("neo4j.dump"),
"/var/lib/neo4j/data/dumps/neo4j.dump")
.withCopyToContainer(Transferable.of("""
#!/bin/bash -eu
/var/lib/neo4j/bin/neo4j-admin database load neo4j
/startup/docker-entrypoint.sh neo4j
""", 0100555), "/startup/load-dump-and-start.sh")
.withCommand("/startup/load-dump-and-start.sh")
.withLogConsumer(f -> System.out.print(f.getUtf8String()));
neo4j.start();
driver = GraphDatabase.driver(neo4j.getBoltUrl(), AuthTokens.basic("neo4j", neo4j.getAdminPassword()));
}
#Test
void dataShouldHaveBeenLoaded() {
try (var session = driver.session()) {
var numNodes = session.run("MATCH (n) RETURN count(n)").single().get(0).asLong();
Assertions.assertTrue(numNodes > 0);
}
}
#AfterAll
void stopNeo4j() {
neo4j.stop();
}
}
Edit:
If you are on a recent enterprise edition, Christophe suggested the following solution on Twitter, which I do personally think it's superior, as it is less hacky.
https://github.com/ikwattro/neo4j-5-testcontainers-restore-backup/blob/main/src/test/java/dev/ikwattro/Neo4j5RestoreBackupExampleTest.java
It makes use of seed URIs for databases. Copying or binding the resource in his example works the same as in mine.
If you're using Neo4j 5, I suggest you make backups instead of dumps. Using backups you can take advantage of the seedUri option when creating a database, meaning you can create a database from an URI pointing to a backup on disk ( or in the neo4j container ). https://neo4j.com/docs/operations-manual/current/clustering/databases/#cluster-seed-uri
Here is an example using Testcontainers
package dev.ikwattro;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.Session;
import org.neo4j.driver.SessionConfig;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import static org.assertj.core.api.Assertions.assertThat;
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#Testcontainers(disabledWithoutDocker = true)
public class Neo4j5RestoreBackupExampleTest {
#Container
private Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:5.3.0-enterprise")
.withAdminPassword("password")
.withEnv("NEO4J_dbms_memory_heap_max__size", "256M")
.withEnv("NEO4J_dbms_databases_seed__from__uri__providers", "URLConnectionSeedProvider")
.withClasspathResourceMapping("backups", "/backups", BindMode.READ_ONLY)
.withEnv("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes");
#BeforeAll
void beforeAll() {
neo4j.start();
createDbFromBackup();
}
#Test
void testCreatingDbFromBackup() {
try (Driver driver = GraphDatabase.driver(neo4j.getBoltUrl(), AuthTokens.basic("neo4j", "password"))) {
try (Session session = driver.session(SessionConfig.forDatabase("worldcup22"))) {
var result = session.run("MATCH (n) RETURN count(n) AS c").single().get("c").asLong();
assertThat(result).isPositive();
}
}
}
private void createDbFromBackup() {
try (Driver driver = GraphDatabase.driver(neo4j.getBoltUrl(), AuthTokens.basic("neo4j", "password"))) {
try (Session session = driver.session(SessionConfig.forDatabase("system"))) {
session.run("""
CREATE DATABASE worldcup22 OPTIONS { existingData: "use", seedUri: "file:///backups/world-cup-2022-neo4j.backup"}
""");
}
}
}
}
You can find a working maven project here https://github.com/ikwattro/neo4j-5-testcontainers-restore-backup
I know how to use two runner classes to rerun failed scenarios, but I want this feature only for one test.
Let's say that I have 100 scenarios and I only want to rerun scenario 40 when it fails, but if any other scenaria fails, I don't want it to rerun. Is there a way to implement this for one test in particular?
To see how to rerun all failed scenarios, check out this question:
How to rerun the failed scenarios using Cucumber?
You'll have to write custom code for this. Fortunately this is relatively easy with the JUnit Platform API (JUnit 5).
https://github.com/cucumber/cucumber-jvm/tree/main/cucumber-junit-platform-engine#rerunning-failed-scenarios
package com.example;
import org.junit.platform.engine.discovery.DiscoverySelectors;
import org.junit.platform.engine.discovery.UniqueIdSelector;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestIdentifier;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
import org.junit.platform.launcher.listeners.TestExecutionSummary.Failure;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectDirectory;
import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
public class RunCucumber {
public static void main(String[] args) {
LauncherDiscoveryRequest request = request()
.selectors(
selectDirectory("path/to/features")
)
.build();
Launcher launcher = LauncherFactory.create();
SummaryGeneratingListener listener = new SummaryGeneratingListener();
launcher.registerTestExecutionListeners(listener);
launcher.execute(request);
TestExecutionSummary summary = listener.getSummary();
// Do something with summary
List<UniqueIdSelector> failures = summary.getFailures().stream()
.map(Failure::getTestIdentifier)
.filter(TestIdentifier::isTest)
// Filter more to select scenarios to rerun
.map(TestIdentifier::getUniqueId)
.map(DiscoverySelectors::selectUniqueId)
.collect(Collectors.toList());
LauncherDiscoveryRequest rerunRequest = request()
.selectors(failures)
.build();
launcher.execute(rerunRequest);
TestExecutionSummary rerunSummary = listener.getSummary();
// Do something with rerunSummary
}
}
I have Spring Boot console application which accepts user input. In the same application, there are service to perform some logic.
On running the service's JUnit test case directly, I am observing a behavior where the console application stays in the state of accepting user input, and thus not entering the JUnit tests.
Usually there is no issue becasue for a normal Spring Boot application, there is nothing to run on start up. But for this console application setup, it awaits user input.
May I know if there is any way that can make the test cases run while not triggering the console application? Thank you very much.
Observation: The console application is triggered on JUnit test case run, test case is not entered
Main application
import java.io.BufferedReader;
import java.io.InputStreamReader;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class ToyRobotApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(ToyRobotApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
// Instruct user to perform input
System.out.println("Welcome to toy robot application!");
System.out.println("Below are the possible operations:");
System.out.println("PLACE <x-coordinate> <y-coordinate> <facing>");
System.out.println("MOVE");
System.out.println("LEFT");
System.out.println("RIGHT");
System.out.println("REPORT");
while (true) {
System.out.println("Please enter your command:");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String usrInput = br.readLine();
//...
}
}
}
Test case
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.somecompany.model.Facing;
import com.somecompany.model.Location;
import com.somecompany.model.Robot;
import com.somecompany.service.ToyRobotService;
#SpringBootTest
public class ToyRobotReportTest {
#Autowired
private Robot robot;
#Autowired
private ToyRobotService toyRobotService;
#Test
public void shouldBeAbleToReportLocation() {
Location location = new Location();
location.setXCor("1");
location.setYCor("2");
location.setFacing(Facing.NORTH);
robot.setLocation(location);
// Actual result
String result = toyRobotService.report();
// Assertion
assertEquals("1,2,NORTH", result);
}
}
Note: Other sources omitted
I got the setup working after some research. To get this to work, the JUnit test class should not be annotated with the usual #SpringBootTest annotation.
When the class is annotated with #SpringBootTest, it will start up the application's context (i.e. start the application). In most usual test scenarios, this should be fine if there is no waiting of user input.
In our case, we want the Spring Boot annotations (e.g. #Autowired) to be recognized, but do not want the application itself to be started up.
We can use the following in JUnit:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = ToyRobotApplication.class, initializers = ConfigDataApplicationContextInitializer.class)
public class ToyRobotPlaceTest {
...
}
This will regonize spring annotations, but will not start the application context.
while creating pact verification test i am using httpTarget Method. but the problem is my service dont have a port value. how can we run this? please advise.
Service URL=http://services.groupkt.com/country/get/iso3code/IND
below is my verification test.
package se.ff.bsv;
import au.com.dius.pact.provider.junit.PactRunner;
import au.com.dius.pact.provider.junit.Provider;
import au.com.dius.pact.provider.junit.State;
import au.com.dius.pact.provider.junit.loader.PactFolder;
import au.com.dius.pact.provider.junit.loader.PactUrl;
import au.com.dius.pact.provider.junit.target.HttpTarget;
import au.com.dius.pact.provider.junit.target.Target;
import au.com.dius.pact.provider.junit.target.TestTarget;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.runner.RunWith;
import java.net.URL;
import java.util.Map;
#RunWith(PactRunner.class) // Say JUnit to run tests with custom Runner
#Provider("getCountryService") // Set up name of tested provider
#PactFolder("../pacts") // Point where to find pacts (See also section Pacts source in documentation)
//#PactUrl(urls = {"http://services.groupkt.com/country/get/iso3code/IND"} )
public class getCountryContractTest {
#State("There is a country with alpha2_code as IN having name as India") // Method will be run before testing
// interactions that require "with-data"
// state
public void hammerSmith() {
System.out.println("There is a country with alpha2_code as IN having name as India");
}
#TestTarget // Annotation denotes Target that will be used for tests
//public final Target target = new HttpTarget(8111);// Out-of-the-box implementation of Target (for more information take a look at Test Target section)
public final Target target = new HttpTarget("http", "services.groupkt.com",);
}
You can't say your service don't have a port, but you can say your service uses a default port (for instance). In that case, try to use port 80.
I have a Spring Shell-based application and a couple of scripts. Is there an easy way to run the scripts in a JUnit test such that a test fails, if some exception/error occurs during the execution of the script?
The purpose of the tests is to make sure that all correct scripts run without errors.
Update 1:
Here's a little helper class for running scripts in JUnit:
import org.apache.commons.io.FileUtils;
import org.springframework.shell.Bootstrap;
import org.springframework.shell.core.CommandResult;
import org.springframework.shell.core.JLineShellComponent;
import java.io.File;
import java.io.IOException;
import java.util.List;
import static org.fest.assertions.api.Assertions.*;
public class ScriptRunner {
public void runScript(final File file) throws IOException
{
final Bootstrap bootstrap = new Bootstrap();
final JLineShellComponent shell = bootstrap.getJLineShellComponent();
final List<String> lines = FileUtils.readLines(file);
for (final String line : lines) {
execVerify(line, shell);
}
}
private void execVerify(final String command, final JLineShellComponent shell) {
final CommandResult result = shell.executeCommand(command);
assertThat(result.isSuccess()).isTrue();
}
}
You can create an instance of Bootstrap, get the shell out of it and then executeCommand() (including the shell command) on it.
You may be interested in what is done in Spring XD for this: https://github.com/spring-projects/spring-xd/blob/master/spring-xd-shell/src/test/java/org/springframework/xd/shell/AbstractShellIntegrationTest.java (although there are a lot of XD specific details)