DEV Community

loading...

Serenity automation framework - Part 3/4 - Automation test for UI with ScreenPlay pattern and jUnit

cuongld2 profile image cuongld2 Updated on ・5 min read

Dear folks, I'm back.

This time, I will continue the series about using Serenity framework for automation with screenplay pattern and jUnit.

You might want to check out details about serenity screenplay pattern in here.

Also, there is a cool book about BDD which I think you should take a look as it will open your mind about how BDD can greatly help you increasing product quality.

Please check this link for BDD in action book (The author is the one who created the Serenity framework).

Please take the below paragraph as my short introduction to Serenity

The Screenplay Pattern is a powerful and elegant approach to designing and implementing automated tests, providing a number of improvements over more traditional approaches such as the Page Objects model. The Screenplay Pattern uses good software engineering principles such as the Single Responsibility Principle, the Open-Closed Principle, favours composition over inheritance, employs thinking from Domain Driven Design to reflect the domain of performing acceptance tests and steers you towards effective use of layers of abstraction. It encourages good testing habits and well-designed test suites that are easy to read, easy to maintain and easy to extend, enabling teams to write more robust and more reliable automated tests more effectively.

Alt Text

Imagine we are in the theater, and watching a play. There will be actors who doing the specific tasks, actions.. That's the core of screenplay pattern.

A customer who interact with the web page is like the actor. They can play a certain roles, doing the specific things they're allowed to do (like normal customer, VIP customer, admin ..etc.)

If you are building the structure and the tasks well, the test framework can be easy to maintain and scale. I think it's better when compared to Cucumber as Cucumber is really hard to maintain when the project is big enough.

Screenplay pattern can combine with the Cucumber as well, but in this tutorial I will not cover that as I myself don't think that's the need if we implement screenplay pattern correctly.

So how do we start implement automation test in screenplay pattern with jUnit.

1.Setup POM file:

we will need some serenity dependencies: serenity with jUnit (to run the screenplay pattern), serenity-ensure ( for matching when we implement task or action), serenity-screenplay for screenplay supported function, and serenity-reports for making beautiful reports with screenshot


<dependencies>
        <!-- https://mvnrepository.com/artifact/net.serenity-bdd/serenity-junit -->
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-junit</artifactId>
            <version>${serenity.maven.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/net.serenity-bdd/serenity-ensure -->
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-ensure</artifactId>
            <version>${serenity.maven.version}</version>
        </dependency>
        <dependency>
        <!-- https://mvnrepository.com/artifact/net.serenity-bdd/serenity-screenplay -->
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-screenplay</artifactId>
            <version>${serenity.maven.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/net.serenity-bdd/serenity-reports -->
        <dependency>
            <groupId>net.serenity-bdd</groupId>
            <artifactId>serenity-reports</artifactId>
            <version>${serenity.maven.version}</version>
        </dependency>
    </dependencies>


Enter fullscreen mode Exit fullscreen mode

2.Project Structure:

In main, there will be 4 packages:

  • questions
  • actions
  • tasks
  • user_interface

Questions is where we store all the classes used to verify something (verify some elements in the web, url...)

In behavior, it's like user should see some button, some text after they do some certain task

Actions is where we store all the classes to do some specific simple thing like click on the button, get the element attribute..

Tasks is where we store all the classes defined to doing specific set of actions (we should make it small enough for better reused later)

User_interface is where we store the targets/locators or page url

Alt Text

3.Do the test:

  • Define the steps we would cover, usually we will do the test first without implementing the task:

@RunWith(SerenityRunner.class)
public class LoginPage {

    private Actor donald = Actor.named("Donald");


    @Managed(uniqueSession = true)
    private WebDriver hisBrowser;

    @Before
    public void annaCanBrowseTheWeb() {
        donald.can(BrowseTheWeb.with(hisBrowser));
    }

    @Test
    public void login_with_correct_credential() {

        givenThat(donald).wasAbleTo(Login.loginThePage("ledinhcuong99@gmail.com",
                "Automationzone1990#"));
        then(donald).should(seeThat(CurrentUrl.information(),containsString("welcome")));
    }

}

Enter fullscreen mode Exit fullscreen mode

Add the annotation @RunWith(SerenityRunner.class) to tell mvn to run the test with serenity jUnit

In the test class, we define actor named Donald. He can browser the web and doing the certain thing like login the page with username and password.

Serenity allow us to define the test in a readable step with givenThat, when, then, along with other DSL like wasAbleTo, attemptTo, should, seeThat...

After implement the needed step, we need to define the user_interfact, action, the task and the question.

  • User_interface:

Set the url for the home page


@DefaultUrl("https://cp.qc.coccoc.com/sign-in?lang=vi-VN")
public class LoginPage extends PageObject {
}

Enter fullscreen mode Exit fullscreen mode

Set the target/locator for element we need to interact with:


public class LoginPageElements {

    public static Target EMAIL_FIELD    =  Target.the("email field")
            .located(By.name("email"));

    public static Target
            PASSWORD_FIELD = Target.the("password field")
            .located(By.name("password"));

    public static Target BTN_LOGIN = Target.the("btn login")
            .located(By.cssSelector("button[data-track_event-action='Login']"));

    public static Target ERROR_MESSAGE_ELEMENT   = Target.the("error message element")
            .located(By.xpath("//form[@method='post'][not(@name)]//div[@class='form-errors clearfix']"));



}

Enter fullscreen mode Exit fullscreen mode
  • Action:

Perform Navigate class to navigate to the Login page


public class Navigate implements Performable {

    public static Performable theRightPlace() {
        return new Navigate();

    }

    @Override
    public <T extends Actor> void performAs(T actor) {
        actor.attemptsTo(
                Open.browserOn().the(ApplicationHomePage.class)
        );

    }

}


Enter fullscreen mode Exit fullscreen mode
  • Task Define how to login the page

public class Login implements Task {

    private final String email_text;
    private final String password_text;

    @Step("{0} Login with email '#email_text' and password '#password_text'")
    @Override
    public <T extends Actor> void performAs(T actor) {

        actor.attemptsTo(Navigate.theRightPlace(),
                Click.on(LoginPageElements.EMAIL_FIELD),
                SendKeys.of(email_text).into(LoginPageElements.EMAIL_FIELD),
                Click.on(LoginPageElements.PASSWORD_FIELD),
                SendKeys.of(password_text).into(LoginPageElements.PASSWORD_FIELD),
                Click.on(LoginPageElements.BTN_LOGIN)
                );

    }

    public Login(String email_text, String password_text){
        this.email_text = email_text;
        this.password_text = password_text;

    }



    public static Login loginThePage(String email_text, String password_text) {
        return instrumented(Login.class
        ,email_text,password_text);
    }

}

Enter fullscreen mode Exit fullscreen mode

Our class have the input variable for the login which are email_text and password text.

This annotation @Step("{0} Login with email '#email_text' and password '#password_text'") to show the detail value in the report

We need to override performAs to implement the necessary steps for login

  • Question:

public class CurrentUrl implements Question<String> {

    public String answeredBy(Actor actor){

        WebDriver driver = getDriver();
        return driver.getCurrentUrl();

    }
    public static CurrentUrl information(){
        return new CurrentUrl();
    }

}

Enter fullscreen mode Exit fullscreen mode

In the question class, we define to get the current url, so that we can assert it is correct or not.

4.Run the test

To run the test with the beautiful report, we will do with mvn command:

mvn clean verify -Dtest=ui.screenplay.qc_coccoc.LoginPage serenity:aggregate

Alt Text

As always, you can find the source code in my github page serenity-guideline

If you like the content, please like or share. It would mean a lot to me.

Feel free to comment if you got anything to say.

Peace!!!

Notes: If you feel this blog help you and want to show the appreciation, feel free to drop by :

This will help me to contributing more valued contents.

Discussion

pic
Editor guide
Collapse
contactkrsna1 profile image
Krsna

Awesome!!! Nice Article
I have a question below
I wrote 10 test suites and developed a gui app which has 10 buttons(I button for each test suite). When i click on a button, a java method is called and from there the corresponding test suite should be called and generate report. is this possible? I am using serenity with cucumber started project