How do I add retry logic to leanft with Junit tests? - java

While LeanFT uses JUnit for its test runner, it doesn't appear to implement 'TestRule'. This excludes a 'standard' method describe elsewhere.
How to Re-run failed JUnit tests immediately?
Anyone have a solution to this?

It seems that you refer to the fact that the report doesn't have the test result.
Indeed it seems it's no longer automatically reported when using TestRule.
However, you can manually report whatever you want to report.
Here's an example of Junit test that reports what we want it to report.
import com.hp.lft.report.CaptureLevel;
import com.hp.lft.report.ReportLevel;
import com.hp.lft.report.Reporter;
import com.hp.lft.sdk.web.Browser;
import com.hp.lft.sdk.web.BrowserFactory;
import com.hp.lft.sdk.web.BrowserType;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.*;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import unittesting.UnitTestClassBase;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
public class RetryTest extends UnitTestClassBase {
#BeforeClass
public static void setUpBeforeClass() throws Exception {
instance = new LeanFtTest();
globalSetup(LeanFtTest.class);
}
#AfterClass
public static void tearDownAfterClass() throws Exception {
globalTearDown();
}
public class Retry implements TestRule {
private int retryCount;
public Retry(int retryCount) {
this.retryCount = retryCount;
}
public Statement apply(Statement base, Description description) {
return statement(base, description);
}
private Statement statement(final Statement base, final Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
Throwable caughtThrowable = null;
// implement retry logic here
for (int i = 0; i < retryCount; i++) {
try {
base.evaluate();
return;
} catch (Throwable t) {
caughtThrowable = t;
System.err.println(description.getDisplayName() + ": run " + (i+1) + " failed");
}
}
System.err.println(description.getDisplayName() + ": giving up after " + retryCount + " failures");
throw caughtThrowable;
}
};
}
}
#Rule
public Retry retry = new Retry(3);
#Test
public void test2() throws Exception{
Reporter.startReportingContext("Reporting for test2");
Reporter.reportEvent("Reporting", "Reporting stuff", Status.Passed);
Reporter.reportEvent("Reporting", "Reporting some more stuff", Status.Failed);
Reporter.endReportingContext();
Object o = null;
o.equals("foo");
}
}
And this is how it looks like:

Related

MiniKdc not available from org.springframework.security.kerberos.test.MiniKdc

I am trying to use "MiniKdc" in my code implementation like "MiniKdc.main(config)" but am getting error "can not resolve symbol 'MiniKdc' ".
I am following this example https://www.baeldung.com/spring-security-kerberos-integration
i have added this dependecy in my build.gradle
implementation 'org.springframework.security.kerberos:spring-security-kerberos-test:1.0.1.RELEASE'
i tried to search the dependecy from maven central/repository and i can't find it.
here is the class i am working on, i want to be able to import Minikdc in the second import statement.
import org.apache.commons.io.FileUtils;
import org.springframework.security.kerberos.test.MiniKdc;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
class KerberosMiniKdc {
private static final String KRB_WORK_DIR = ".\\spring-security-sso\\spring-security-sso-kerberos\\krb-test-workdir";
public static void main(String[] args) throws Exception {
String[] config = MiniKdcConfigBuilder.builder()
.workDir(prepareWorkDir())
.confDir("minikdc-krb5.conf")
.keytabName("example.keytab")
.principals("client/localhost", "HTTP/localhost")
.build();
MiniKdc.main(config);
}
private static String prepareWorkDir() throws IOException {
Path dir = Paths.get(KRB_WORK_DIR);
File directory = dir.normalize().toFile();
FileUtils.deleteQuietly(directory);
FileUtils.forceMkdir(directory);
return dir.toString();
}
}
is there anything am doing wrong?
As of 2021, spring-security-kerberos is not well maintained.
I suggest using Apache Kerby instead, either directly or via other library like Kerb4J. See an example here.
package com.kerb4j;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.kerby.kerberos.kerb.client.KrbConfig;
import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import java.io.File;
public class KerberosSecurityTestcase {
private static final Log log = LogFactory.getLog(KerberosSecurityTestcase.class);
private static int i = 10000;
protected int kdcPort;
private SimpleKdcServer kdc;
private File workDir;
private KrbConfig conf;
#BeforeAll
public static void debugKerberos() {
System.setProperty("sun.security.krb5.debug", "true");
}
#BeforeEach
public void startMiniKdc() throws Exception {
kdcPort = i++;
createTestDir();
createMiniKdcConf();
log.info("Starting Simple KDC server on port " + kdcPort);
kdc = new SimpleKdcServer(workDir, conf);
kdc.setKdcPort(kdcPort);
kdc.setAllowUdp(false);
kdc.init();
kdc.start();
}
#AfterEach
public void stopMiniKdc() throws Exception {
log.info("Stopping Simple KDC server on port " + kdcPort);
if (kdc != null) {
kdc.stop();
log.info("Stopped Simple KDC server on port " + kdcPort);
}
}
public void createTestDir() {
workDir = new File(System.getProperty("test.dir", "target"));
}
public void createMiniKdcConf() {
conf = new KrbConfig();
}
public SimpleKdcServer getKdc() {
return kdc;
}
public File getWorkDir() {
return workDir;
}
public KrbConfig getConf() {
return conf;
}
}
Disclaimer: I'm the author of Kerb4J

Mock whenever new instance created without PowerMockito JUnit5

JUnit5 does not support PowerMockRunner hence the following code will not work whenever you migrate from JUnit4 to JUnit5.
Eg.
Code you trying to inject mock
import javax.naming.InvalidNameException;
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.publish();
}
public void publish() {
try {
Sample s = new Sample();
s.invoke("Hello");
} catch (InvalidNameException e) {
throw new ServiceFailureException(e.getMessage());
}
}
}
Here you are trying to test publish method where you mock the Sample instance to respond with different responses.
In JUnit4 you could have use PowerMockito to achieve that.
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import javax.naming.InvalidNameException;
#RunWith(PowerMockRunner.class)
#PrepareForTest({Main.class})
public class MainTest {
#Test
public void testPublishSuccess() {
Main m = new Main();
Assert.assertEquals("Expected result not found", "success", m.publish());
}
#Test
public void testPublishFailure() throws Exception{
Sample sample = new Sample();
PowerMockito.when(sample.invoke(Mockito.anyString())).thenReturn("failure");
PowerMockito.whenNew(Sample.class).withNoArguments().thenReturn(sample);
Main m = new Main();
Assert.assertEquals("Expected result not found", "failure", m.publish());
}
#Test(expected = ServiceFailureException.class)
public void testPublishException() throws Exception{
Sample sample = new Sample();
PowerMockito.when(sample.invoke(Mockito.anyString())).thenThrow(new InvalidNameException("Invalid name provided"));
PowerMockito.whenNew(Sample.class).withNoArguments().thenReturn(sample);
Main m = new Main();
m.publish();
}
}
With the introduction of JUnit5, the test cases are failing at mock creating new instances because PowerMockRunner does not support JUnit5.
What is the alternate for using PowerMockito with JUnit5.
As PowerMockito does not support JUnit5, we can use Mockito inline. Here is the code which replace the PowerMockito.
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import javax.naming.InvalidNameException;
public class MainTestJunit5 {
#Test
public void testPublishSuccess() {
Main m = new Main();
Assertions.assertEquals("success", m.publish(), "Expected result not found");
}
#Test
public void testPublishFailure() throws Exception{
try (MockedConstruction<Sample> mockedConstruction = Mockito.mockConstruction(Sample.class, (sampleMock, context) -> {
Mockito.when(sampleMock.invoke(Mockito.anyString())).thenReturn("failure");
})) {
Sample sample = new Sample();
PowerMockito.when(sample.invoke(Mockito.anyString())).thenReturn("failure");
PowerMockito.whenNew(Sample.class).withNoArguments().thenReturn(sample);
Main m = new Main();
Assertions.assertEquals("Expected result not found", "failure", m.publish());
}
}
#Test
public void testPublishException() throws Exception{
try (MockedConstruction<Sample> mockedConstruction = Mockito.mockConstruction(Sample.class, (sampleMock, context) -> {
Mockito.when(sampleMock.invoke(Mockito.anyString())).thenThrow(new InvalidNameException("Invalid name found"));
})){
Main m = new Main();
boolean error = false;
try {
m.publish();
} catch (ServiceFailureException e) {
error = true;
}
Assertions.assertTrue(error, "Exception throwing expected");
}
}
}
Couple of things you need to pay attention
Setting up mockito-inline need additional dependency and an additional configuration.
Extra test runners (PowerMockRunner) and preparation for testing is not needed.
MockedConstruction is scoped, so you have to put all the mocking and processing done within that code block.
JUnit5 messages are the final method argument.
Mockito documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#49

Basic Pact/Junit5 Test Setup fails. No method annotated with #Pact was found for provider error

I tried to follow the documentation on Pact.io to write a simple integration test.
Unfortunately i get an exception as follows:
org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [au.com.dius.pact.consumer.MockServer mockServer] in method [public void com.example.demo.integration.pact.PactTest.setUp(au.com.dius.pact.consumer.MockServer)]: No method annotated with #Pact was found on test class PactTest for provider 'node_server'
It says that I don't have any method annotated with #Pact. However I do have a method, which is annotated with #Pact.
I tried to run the test manually and with 'mvn test' as well.
The application in general is providing some Rest Controllers, which should be tested.
Following is all I have implemented regarding my Pact Test Implementation. Am I missing something?
package com.example.demo.integration.pact;
import au.com.dius.pact.consumer.MockServer;
import au.com.dius.pact.consumer.dsl.PactDslWithProvider;
import au.com.dius.pact.consumer.junit5.PactConsumerTestExt;
import au.com.dius.pact.consumer.junit5.PactTestFor;
import au.com.dius.pact.core.model.RequestResponsePact;
import au.com.dius.pact.core.model.annotations.Pact;
import org.apache.http.HttpResponse;
import org.apache.http.client.fluent.Request;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.assertEquals;
#ExtendWith(PactConsumerTestExt.class)
#PactTestFor(providerName = PactTest.PACT_PROVIDER_NAME)
public class PactTest {
public static final String PACT_PROVIDER_NAME = "node_server";
public static final String PACT_CONSUMER_NAME = "spring_application";
#BeforeEach
public void setUp(MockServer mockServer) {
System.out.println("Mockserver check called");
Assertions.assertTrue(mockServer != null);
}
#Pact(provider = PACT_PROVIDER_NAME, consumer = PACT_CONSUMER_NAME)
public RequestResponsePact createPact(PactDslWithProvider builder) {
return builder
.uponReceiving("notes")
.path("/notes")
.method("GET")
.willRespondWith()
.matchHeader("Content-Type","application/json")
.status(200)
.body(
getJsonArrayOfNotes(2).toString())
.toPact();
}
#Test
#PactTestFor(pactMethod = "notes")
void test(MockServer mockServer) throws IOException {
HttpResponse httpResponse = Request.Get(mockServer.getUrl() + "/notes").execute().returnResponse();
assertEquals(200, httpResponse.getStatusLine().getStatusCode());
assertEquals(getJsonArrayOfNotes(2).toString(),httpResponse.getEntity().getContent().toString());
}
private JSONArray getJsonArrayOfNotes(int size) {
var responseJsonObject = new JSONArray();
for (int i = 0; i < size; i++) {
var note = new JSONObject();
try {
note.put("title", String.format("Title %s", i + 1));
note.put("content", String.format("Some Note Content of Note %s", i + 1));
} catch (Exception exception) {
}
responseJsonObject.put(note);
}
return responseJsonObject;
}
}
Seems like the method name with the #Pact annotation must be the same as the pactMethod in the #PactTestFor annotation...
In my case I had to write following:
#Test
#PactTestFor(pactMethod = "getNotes")
void test(MockServer mockServer) throws IOException {
HttpResponse httpResponse = Request.Get(mockServer.getUrl() + "/notes").execute().returnResponse();
assertEquals(200, httpResponse.getStatusLine().getStatusCode());
assertEquals(getJsonArrayOfNotes(2).toString(),httpResponse.getEntity().getContent().toString());
}
#Pact(provider = PACT_PROVIDER_NAME, consumer = PACT_CONSUMER_NAME)
public RequestResponsePact getNotes(PactDslWithProvider builder) {
return builder
.uponReceiving("notes")
.path("/notes")
.method("GET")
.willRespondWith()
.matchHeader("Content-Type","application/json")
.status(200)
.body(
getJsonArrayOfNotes(2).toString())
.toPact();
}

Why does Amazon SWF Workflow not cancel immediately after calling TryCatch.cancel()?

I am using the below code sample where I am calling the cancelWF method to cancel the execution of workflow. The onCatch method is successfully invoked with the RuntimeException("Simply cancel"), but on the Amazon SWF console the WF does not end immediately, it waits will timeout and ends with a WorkflowExecutionTerminated event.
The whole project is available here if you want more info.
package aws.swf;
import aws.swf.common.Constants;
import aws.swf.common.DelayRequest;
import aws.swf.common.MyActivityClient;
import aws.swf.common.MyActivityClientImpl;
import aws.swf.common.MyWorkflow;
import aws.swf.common.SWFClient;
import com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflow;
import com.amazonaws.services.simpleworkflow.flow.WorkflowWorker;
import com.amazonaws.services.simpleworkflow.flow.annotations.Asynchronous;
import com.amazonaws.services.simpleworkflow.flow.core.Promise;
import com.amazonaws.services.simpleworkflow.flow.core.TryCatch;
import java.util.concurrent.CancellationException;
public class D_CancelWorkflow implements MyWorkflow {
private TryCatch tryCatch;
private final MyActivityClient activityClient = new MyActivityClientImpl();
#Override
public void sum() {
tryCatch = new TryCatch() {
#Override
protected void doTry() throws Throwable {
System.out.printf("[WF %s] Started exec\n", D_CancelWorkflow.this);
Promise<Integer> result = activityClient.getNumWithDelay(new DelayRequest("req1", 1));
cancelWF(result);
newDelayRequest(result);
}
#Override
protected void doCatch(Throwable e) throws Throwable {
if (e instanceof CancellationException) {
System.out.printf("[WF %s] Cancelled With message [%s]\n",
D_CancelWorkflow.this, e.getCause().getMessage());
} else {
e.printStackTrace();
}
rethrow(e);
}
};
}
#Asynchronous
private void newDelayRequest(Promise<Integer> num) {
activityClient.getNumWithDelay(new DelayRequest("req2", 1));
}
#Asynchronous
private void cancelWF(Promise<Integer> ignore) {
System.out.printf("[WF %s] Cancelling WF\n", D_CancelWorkflow.this);
this.tryCatch.cancel(new RuntimeException("Simply cancel"));
}
public static void main(String[] args) throws Exception {
AmazonSimpleWorkflow awsSwfClient = new SWFClient().getClient();
WorkflowWorker workflowWorker =
new WorkflowWorker(awsSwfClient, Constants.DOMAIN, Constants.TASK_LIST);
workflowWorker.addWorkflowImplementationType(D_CancelWorkflow.class);
workflowWorker.start();
}
}
This is the event history for one of my execution,

How take screenshot and attach it to Allure report, while using Cucumber and JUnit?

I'm using Cucumber, Selenium, Java, Maven and JUnit stack in my automation-test-project.
The goal is to take screenshots on fails and broken tests. I have found the solution for Java/Maven/JUnit stack:
#Rule
public TestWatcher screenshotOnFailure = new TestWatcher() {
#Override
protected void failed(Throwable e, Description description) {
makeScreenshotOnFailure();
}
#Attachment("Screenshot on failure")
public byte[] makeScreenshotOnFailure() {
return ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
}
};
But, of course it does not work in case of using Cucumber, because it does not use any #Test methods.
So, I've decided to change #Rule to #ClassRule, to make it listen to any fails, so here it is:
#ClassRule
public static TestWatcher screenshotOnFailure = new TestWatcher() {
#Override
protected void failed(Throwable e, Description description) {
makeScreenshotOnFailure();
}
#Attachment("Screenshot on failure")
public byte[] makeScreenshotOnFailure() {
logger.debug("Taking screenshot");
return ((TakesScreenshot) Application.getInstance().getWebDriver()).getScreenshotAs(OutputType.BYTES);
}
};
And this solution didn't help me.
So, the question is: "How to attach screenshots on fail, when I use Java/Selenium/Cucumber/JUnit/Maven in my test project?"
The solution is just to add following code to your definition classes:
#After
public void embedScreenshot(Scenario scenario) {
if (scenario.isFailed()) {
try {
byte[] screenshot = ((TakesScreenshot) Application.getInstance().getWebDriver())
.getScreenshotAs(OutputType.BYTES);
scenario.embed(screenshot, "image/png");
} catch (Exception e) {
e.printStackTrace();
}
}
}
In the GlobalGlue
public class GlobalGlue {
#Before
public void before(Scenario scenario) throws Exception {
CONTEXT.setScenario(scenario);
}
#After
public void after() {
WebDriverUtility.after(getDriver(), CONTEXT.getScenario());
}
}
Create another class WebDriverUtility and in that add method:
public static void after(WebDriver driver, Scenario scenario) {
getScreenshot(driver, scenario);
driver.close();
}
and
public static void getScreenshot(WebDriver driver, Scenario scenario) {
if (scenario.isFailed()) {
final byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
scenario.embed(screenshot, "image/png");
log.info("Thread: " + Thread.currentThread().getId() + " :: "
+ "Screenshot taken and inserted in scenario report");
}
}
the main part is you need to embed the screen shot in scenario when scenario is failed:
final byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
scenario.embed(screenshot, "image/png");
ExecutionContext.java
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import cucumber.api.Scenario;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openqa.selenium.WebDriver;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
/**
* Maintains one webdriver per scenario and one scenario per thread.
* Can be used for parallel execution.
* Assumes that scenarios within one feature are not parallel.
* Can be rewritten using <code>ThreadLocal</code>.
*
* #author dkhvatov
*/
public enum ExecutionContext {
CONTEXT;
private static final Logger log = LogManager.getLogger(ExecutionContext.class);
private final LoadingCache<Scenario, WebDriver> webDrivers =
CacheBuilder.newBuilder()
.build(CacheLoader.from(scenario ->
WebDriverUtility.createDriver()));
private final Map<String, Scenario> scenarios = new ConcurrentHashMap<>();
/**
* Lazily gets a webdriver for the current scenario.
*
* #return webdriver
*/
public WebDriver getDriver() {
try {
Scenario scenario = getScenario();
if (scenario == null) {
throw new IllegalStateException("Scenario is not set for context. " +
"Please verify your glue settings. Either use GlobalGlue, or set " +
"scenario manually: CONTEXT.setScenario(scenario)");
}
return webDrivers.get(scenario);
} catch (ExecutionException e) {
log.error("Unable to start webdriver", e);
throw new RuntimeException(e);
}
}
/**
* Gets scenario for a current thread.
*
* #return scenario
*/
public Scenario getScenario() {
return scenarios.get(Thread.currentThread().getName());
}
/**
* Sets current scenario. Overwrites current scenario in a map.
*
* #param scenario scenario
*/
public void setScenario(Scenario scenario) {
scenarios.put(Thread.currentThread().getName(), scenario);
}
}
This way you can attach screens to your allure report.
Also use the Latest allure version in your pom file
import com.google.common.io.Files;
import io.qameta.allure.Attachment;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import java.io.File;
import java.io.IOException;
public class ScreenshotUtils {
#Attachment(type = "image/png")
public static byte[] screenshot(WebDriver driver)/* throws IOException */ {
try {
File screen = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
return Files.toByteArray(screen);
} catch (IOException e) {
return null;
}
}
}
Pom File :
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-cucumber4-jvm</artifactId>
<version>${allure.version}</version>
</dependency>
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-junit4</artifactId>
<version>${allure.version}</version>
<scope>test</scope>
</dependency>
public static void TakeScreenshot(string text)
{
byte[] content = ((ITakesScreenshot)driver).GetScreenshot().AsByteArray;
AllureLifecycle.Instance.AddAttachment(text, "image/png", content);
}
public static void Write(By by, string text)
{
try
{
driver.FindElement(by).SendKeys(text);
TakeScreenshot( "Write : " + text);
}
catch (Exception ex)
{
TakeScreenshot( "Write Failed : " + ex.ToString());
Assert.Fail();
}
}

Categories

Resources