Giter Site home page Giter Site logo

evpl / xteps Goto Github PK

View Code? Open in Web Editor NEW
8.0 1.0 0.0 724 KB

High-level contextual steps in your tests for any reporting tool

License: Apache License 2.0

Java 100.00%
java logging allure reportportal automation testing-tools reporting reporting-tool steps kotlin

xteps's Introduction

Xteps

This project is archived. New version - https://github.com/evpl/xteps2

High-level contextual steps in your tests for any reporting tool

Maven Central Javadoc License Hits-of-Code Lines of code

Xteps is a facade that provides a convenient way to report test steps. Integrations with Allure, Qase, TestIT and ReportPortal are ready, but you can write your own listener for another reporting system or just create an issue.

Table of Contents

How to use

Requires Java 8+ version or Kotlin JVM. Just add suitable dependency.

Java (checked exceptions are not ignored) Java (checked exceptions are ignored) / Kotlin JVM
Allure xteps-allure

Maven Central Javadoc
unchecked-xteps-allure

Maven Central Javadoc
Qase xteps-qase

Maven Central Javadoc
unchecked-xteps-qase

Maven Central Javadoc
ReportPortal xteps-reportportal

Maven Central Javadoc
unchecked-xteps-reportportal

Maven Central Javadoc
TestIT xteps-testit

Maven Central Javadoc
unchecked-xteps-testit

Maven Central Javadoc
Custom reporting system xteps

Maven Central Javadoc
unchecked-xteps

Maven Central Javadoc

Maven:

<dependency>
  <groupId>com.plugatar.xteps</groupId>
  <artifactId>{artifact name from table}</artifactId>
  <version>5.8</version>
  <scope>test</scope>
</dependency>

Gradle:

dependencies {
    testImplementation 'com.plugatar.xteps:{artifact name from table}:5.8'
}

Kotlin DSL:

dependencies {
    testImplementation("com.plugatar.xteps:{artifact name from table}:5.8")
}

Read more about checked exceptions and integrations.

API

Static methods

First part of Xteps API is a set of step, stepTo and threadHook static methods located in the com.plugatar.xteps.checked.Xteps / com.plugatar.xteps.unchecked.UncheckedXteps class.

class ExampleTest {

    @Test
    void staticMethodsExample() {
        WebDriver driver = stepTo("Create WebDriver", () -> {
            /* ... */
            return new ChromeDriver();
        });
        threadHook(driver::quit);
        step("Login", () -> {
            step("Open page", () ->
                driver.navigate().to("https://.../login")
            );
            step("Type login", () ->
                driver.findElement(By.id("login_field")).sendKeys("user123")
            );
            step("Type password", () ->
                driver.findElement(By.id("password_field")).sendKeys("1234567890")
            );
            step("Click on login button", () ->
                driver.findElements(By.id("login_button"))
            );
        });
        final Connection dbConnection = stepTo("Get database connection", () -> {
            /* ... */
            return getDatabaseConnection();
        });
        threadHook(dbConnection::close);
        step("Check user full name label", () -> {
            final By usernameLabelLocator = By.id("full_name_label");
            new WebDriverWait(driver, Duration.ofSeconds(5)).until(visibilityOfElementLocated(usernameLabelLocator));
            final String databaseFullName = dbConnection.createStatement()
                .executeQuery("select full_name from users where username = user123")
                .getString("full_name");
            final String uiFullName = driver.findElement(usernameLabelLocator).getText();
            assertEquals(uiFullName, databaseFullName);
        });
    }
}

Steps chain

Second part is a steps chain starts with stepsChain, stepsChainOf, stepsChainOf(T), stepsChainOf(T1, T2) and stepsChainOf(T1, T2, T3) static method located in the com.plugatar.xteps.checked.Xteps / com.plugatar.xteps.unchecked.UncheckedXteps class.

class ExampleTest {

    @Test
    void stepsChainMethodExample() {
        stepsChain()
            .stepToCtx("Create WebDriver", () -> {
                /* ... */
                return new ChromeDriver();
            })
            .chainHook(WebDriver::quit)
            .nestedSteps("Login", chain -> chain
                .step("Open page", driver ->
                    driver.navigate().to("https://.../login")
                )
                .step("Type login", driver ->
                    driver.findElement(By.id("login_field")).sendKeys("user123")
                )
                .step("Type password", driver ->
                    driver.findElement(By.id("password_field")).sendKeys("1234567890")
                )
                .step("Click on login button", driver ->
                    driver.findElements(By.id("login_button"))
                )
            )
            .stepToCtx("Get database connection", () -> {
                /* ... */
                return getDatabaseConnection();
            })
            .chainHook(Connection::close)
            .step("Check user full name label", (dbConnection, driver) -> {
                final By usernameLabelLocator = By.id("full_name_label");
                new WebDriverWait(driver, Duration.ofSeconds(5)).until(visibilityOfElementLocated(usernameLabelLocator));
                final String databaseFullName = dbConnection.createStatement()
                    .executeQuery("select full_name from users where username = user123")
                    .getString("full_name");
                final String uiFullName = driver.findElement(usernameLabelLocator).getText();
                assertEquals(uiFullName, databaseFullName);
            })
            .callChainHooks();
    }
}

Step objects

Third part is a set of steps objects located in the com.plugatar.xteps.checked.stepobject / com.plugatar.xteps.unchecked.stepobject package.

class CreateWebDriver extends SupplierStep<WebDriver> {

    public CreateWebDriver() {
        super("Create WebDriver", () -> {
            /* ... */
            return new ChromeDriver();
        });
    }
}

class OpenPage extends ConsumerStep<WebDriver> {

    public OpenPage(String url) {
        super("Open page: " + url, driver ->
            driver.navigate().to(url)
        );
    }
}

class TypeLogin extends ConsumerStep<WebDriver> {

    public TypeLogin(String login) {
        super("Type login: " + login, driver ->
            driver.findElement(By.id("login_field")).sendKeys(login)
        );
    }
}

class TypePassword extends ConsumerStep<WebDriver> {

    public TypePassword(String password) {
        super("Type password: " + password, driver ->
            driver.findElement(By.id("password_field")).sendKeys(password)
        );
    }
}

class ClickOnLoginButton extends ConsumerStep<WebDriver> {

    public ClickOnLoginButton() {
        super("Click on login button", driver ->
            driver.findElements(By.id("login_button"))
        );
    }
}

class DatabaseConnection extends SupplierStep<Connection> {

    public DatabaseConnection() {
        super("Get database connection", () -> {
            /* ... */
            return getDatabaseConnection();
        });
    }
}

class CheckUserFullName extends BiConsumerStep<Connection, WebDriver> {

    public CheckUserFullName(String username) {
        super("Check user full name", (dbConnection, driver) -> {
            final By usernameLabelLocator = By.id("full_name_label");
            new WebDriverWait(driver, Duration.ofSeconds(5)).until(visibilityOfElementLocated(usernameLabelLocator));
            final String databaseFullName = dbConnection.createStatement()
                .executeQuery("select full_name from users where username = " + username)
                .getString("full_name");
            final String uiFullName = driver.findElement(usernameLabelLocator).getText();
            assertEquals(uiFullName, databaseFullName);
        });
    }
}

public class ExampleTest {

    @Test
    void stepObjectsExample() {
        stepsChain()
            .stepToCtx(new CreateWebDriver())
            .chainHook(WebDriver::quit)
            .step(new OpenPage("https://.../login"))
            .step(new TypeLogin("user123"))
            .step(new TypePassword("1234567890"))
            .step(new ClickOnLoginButton())
            .stepToCtx(new DatabaseConnection())
            .chainHook(Connection::close)
            .step(new CheckUserFullName("user123"))
            .callChainHooks();
    }
}

Steps can be combined.

class LoginAs extends ConsumerStep<WebDriver> {

    public LoginAs(String username, String password) {
        super("Login as " + username + " / " + password, driver ->
            stepsChainOf(driver)
                .step(new OpenPage("https://.../login"))
                .step(new TypeLogin(username))
                .step(new TypePassword(password))
                .step(new ClickOnLoginButton())
        );
    }
}

You can use stubs before actually implementing the steps.

@Test
void dummyStepObjectsExample() {
    stepsChain()
        .stepToCtx(SupplierStep.<WebDriver>dummy("Create WebDriver"))
        .chainHook(WebDriver::quit)
        .step(RunnableStep.dummy("Open page https://.../login"))
        .step(RunnableStep.dummy("Type login user123"))
        .step(RunnableStep.dummy("Type password 1234567890"))
        .step(RunnableStep.dummy("Click on login button"))
        .stepToCtx(SupplierStep.<Connection>dummy("Get database connection"))
        .chainHook(Connection::close)
        .step(RunnableStep.dummy("Check user full name"))
        .callChainHooks();
}

BDD style example.

class OpenPage extends ConsumerStep<WebDriver> {

    public OpenPage(String url) {
        super("I open page " + url, driver ->
            driver.navigate().to(url)
        );
    }
}

/*
...
 */

public class ExampleTest {

    @Test
    void bddStyleExample() {
        stepsChain()
            .withCtx(() -> {
                /* ... */
                return new ChromeDriver();
            })
            .chainHook(WebDriver::quit)
            .step("When", new OpenPage("https://.../login"))
            .step("And", new TypeLogin("user123"))
            .step("And", new TypePassword("1234567890"))
            .step("And", new ClickOnLoginButton())
            .step("Then", new UserFullNameIs("Expected Name"))
            .callChainHooks();
    }
}

Parameters

There are two ways to load parameters. Be aware that higher source override lower one - properties from file can be overridden by system properties.

Priority Source
1 System properties
2 Properties file (xteps.properties)

Properties list

Name Type Required Default value Description
xteps.enabled Boolean No true Enable/disable steps logging.
xteps.spi Boolean No true Enable/disable Service Provider Interface mechanism to detect and instantiate com.plugatar.xteps.base.StepListener implementations. Implementations should have zero-argument public constructor.
xteps.listeners String No List of com.plugatar.xteps.base.StepListener implementations names in Class#getTypeName() format. Names should be separated by ,. Implementations should have zero-argument public constructor.
xteps.cleanStackTrace Boolean No true Removes all stack trace lines about Xteps from any exception except XtepsException.
xteps.defaultHooksOrder Enum No FROM_LAST The order in which chain and thread hooks of the same priority will be called - FROM_FIRST / FROM_LAST.
xteps.threadHooksThreadInterval Long No 100 Interval between thread hooks daemon thread executions in milliseconds.
xteps.threadHooksThreadPriority Integer No 5 Thread hooks daemon thread priority in the range 1 to 10.

Examples

xteps.properties file example:

xteps.enabled=true
xteps.spi=true
xteps.listeners=com.my.prj.StepListenerImpl1,com.my.prj.StepListenerImpl2
xteps.cleanStackTrace=true
xteps.defaultHooksOrder=FROM_LAST
xteps.threadHooksThreadInterval=100
xteps.threadHooksThreadPriority=5

Additional features

Steps chain hooks

You can use hooks in a steps chain. Hooks will be called in case of any exception in steps chain or in case of callChainHooks method call. You also can use hooks to release resources.

stepsChain().withCtx(new AutoCloseableImpl())
    .chainHook(AutoCloseable::close)
    .step("Step", ctx -> {
        //...
    })
    .callChainHooks();

Thread hooks

You can use thread hooks. Hooks will be called after current thread is finished. You also can use hooks to release resources.

stepsChain().withCtx(new AutoCloseableImpl())
    .threadHook(AutoCloseable::close)
    .step("Step", ctx -> {
        //...
    });

Clean stack trace

cleanStackTrace option is enabled by default. It allows to clear the stack trace from Xteps calls.

final class ExampleTest {

    @Test
    void test() {
        step("Step", () -> {
            //...
            step("Nested step", () -> {
                //...
                throw new RuntimeException("Nested step exception");
            });
        });
    }
}

Stacktrace will look like this.

java.lang.RuntimeException: Nested step exception

	at my.project.ExampleTest.lambda$null$0(ExampleTest.java:15)
	at my.project.ExampleTest.lambda$test$1(ExampleTest.java:13)
	at my.project.ExampleTest.test(ExampleTest.java:11)
	...

Checked exceptions

Xteps is tolerant to checked exceptions. Each method allows you to throw generic exception, the type of this exception is defined by the step body.

@Test
void test() throws MalformedURLException {
    step("Step", () -> {
        //...
        step("Nested step", () -> {
            new URL("abc");
        });
    });
}

You can choose a mechanism to cheat checked exceptions if you want to hide them. Xteps give you the built-in static methods by com.plugatar.xteps.base functional interfaces.

@Test
void test() {
    step("Step", () -> {
        //...
        step("Nested step", ThrowingRunnable.unchecked(() -> {
            new URL("abc");
        }));
    });
}

Unchecked Xteps just hides any exceptions.

@Test
void test() {
    step("Step", () -> {
        //...
        step("Nested step", () -> {
            new URL("abc");
        });
    });
}

Integrations

Set up your project with Allure / Qase / ReportPortal and then just add suitable Xteps dependency.

You can use step name or step description replacements by the way provided by Allure / Qase / TestIT / ReportPortal.

stepsChain()
    .withCtx("value")
    .withCtx(111)
    .step("Step with context = {0} and second context = {1}", (integer, string) -> {
        //...
    });

This step will be reported with name "Step with context = 111 and second context = value".

You can also use utility methods for Allure, Qase and TestIT - AllureStepUtils, QaseStepUtils, TestITStepUtils. It allows you to change the step name and other step attributes at runtime.

JDK 8 unreported exception bug

You may run into a problem if you use Xteps and JDK 8. The issue is caused by generic exceptions.

stepsChain()
    .nestedStepsTo("Step", chain -> chain
        .step("Nested step", () -> {})
    );

This code can fail build with this exception java: unreported exception java.lang.Throwable; must be caught or declared to be thrown.

You can switch to JDK 9+ or switch to Unchecked Xteps or use functional interfaces static methods to hide any exceptions.

stepsChain()
    .nestedStepsTo("Step", ThrowingFunction.unchecked(chain -> chain
        .step("Nested step", () -> {})
    ));

xteps's People

Contributors

dependabot[bot] avatar evpl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

xteps's Issues

Add nestedSteps and nestedSteps methods without input argument in lambda

The problem
Now you must specify lamda input argument even if you don't want to use it.

Code style 1 (argument needed):

nestedSteps("Step 1", steps -> steps
    .step("Inner step 1", () -> {})
    .step("Inner step 2", () -> {})
);

Code style 2 (argument no needed):

nestedSteps("Step 1", steps -> {
    step("Inner step 1", () -> {});
    step("Inner step 2", () -> {});
});

Environment

  • Xteps version 1.1

Remove redundant methods of Xteps class

The problem
Xteps class contais 3 redundant methods: stepToContext, nestedSteps, nestedStepsTo

Instead of nestedSteps and nestedStepsTo methods it is easy to use step and stepTo combination like

step("Step 1", () -> {
    step("Inner step 1", () -> {
        ...
    });
});

Instead of stepToContext method it easy to use steps().stepToContext() combination.

Environment

  • Xteps version 1.2

Add a setting for clean stack trace

The problem
The stack trace contains a lot of redundant information (like DefaultStepWriter methods).

StackTrace example

java.lang.RuntimeException
	at com.my.example.ExampleTest.lambda$test$0(ExampleTest.java:11)
	at com.plugatar.xteps.core.writer.DefaultStepWriter.lambda$writeRunnableStep$1(DefaultStepWriter.java:60)
	at com.plugatar.xteps.core.writer.DefaultStepWriter.writeFunctionStep(DefaultStepWriter.java:105)
	at com.plugatar.xteps.core.writer.DefaultStepWriter.writeRunnableStep(DefaultStepWriter.java:59)
	at com.plugatar.xteps.core.steps.NoCtxStepsOf.step(NoCtxStepsOf.java:86)
	at com.plugatar.xteps.Xteps.step(Xteps.java:258)
	at com.my.example.ExampleTest.test(ExampleTest.java:11)
       ...

Environment

  • Xteps version 1.0
  • Java version 1.8

Add missing javadoc description

The problem

[WARNING] Javadoc Warnings
[WARNING] D:\git\xteps\xteps\src\main\java\com\plugatar\xteps\core\CtxSteps.java:53: warning: no @param for <R>
[WARNING] <R, TH extends Throwable> R applyContextTo(
[WARNING] ^
[WARNING] D:\git\xteps\xteps\src\main\java\com\plugatar\xteps\core\MemorizingCtxSteps.java:55: warning: no @param for <R>
[WARNING] <R, TH extends Throwable> R applyContextTo(
[WARNING] ^
[WARNING] D:\git\xteps\xteps\src\main\java\com\plugatar\xteps\core\StepListener.java:45: warning: no @param for throwable
[WARNING] void stepFailed(String uuid, String stepName, Throwable throwable);
[WARNING] ^
[WARNING] D:\git\xteps\xteps\src\main\java\com\plugatar\xteps\core\StepWriter.java:65: warning: no @param for input
[WARNING] <T, TH extends Throwable> void writeConsumerStep(
[WARNING] ^
[WARNING] D:\git\xteps\xteps\src\main\java\com\plugatar\xteps\core\StepWriter.java:82: warning: no @return
[WARNING] <T, TH extends Throwable> T writeSupplierStep(
[WARNING] ^
[WARNING] D:\git\xteps\xteps\src\main\java\com\plugatar\xteps\core\StepWriter.java:99: warning: no @param for input
[WARNING] <T, R, TH extends Throwable> R writeFunctionStep(
[WARNING] ^
[WARNING] D:\git\xteps\xteps\src\main\java\com\plugatar\xteps\core\StepWriter.java:99: warning: no @return
[WARNING] <T, R, TH extends Throwable> R writeFunctionStep(
[WARNING] ^
[WARNING] D:\git\xteps\xteps\src\main\java\com\plugatar\xteps\core\writer\DefaultStepWriter.java:46: warning: no @param for cleanStackTrace
[WARNING] public DefaultStepWriter(final StepListener listener, final boolean cleanStackTrace) {
[WARNING] ^

Environment

  • Xteps version 1.2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.