Home TDD UI tests:Cucumber+ Selenide

UI tests:Cucumber+ Selenide

by admin

Part 2

Continued articles About writing UI tests on Cucumber With the help of Selenide In the first part, the simplest example of a smoke test for riskmarket.ru In this part we upgrade the test to a full project with reports, talk about screenshots, custom Condition , annotate the elements, enter PageObject

The resulting project can be used as a foundation for your UI tests.

Github project

Video of the test on youtube

Project Structure :

UI tests:Cucumber+ Selenide

Use Intellij IDEA , Maven and Junit

In mail.txt logins, passwords of accounts to work with the test are written. ATTENTION : if you run it on your own, keep in mind that the system will drop one of the users who will be logged in with the same username/password.Change the mailbox.

Dependency in pom.xml:

<dependencies><dependency><groupId> com.codeborne</groupId><artifactId> selenide</artifactId><version> 3.5</version></dependency><dependency><groupId> info.cukes</groupId><artifactId> cucumber-java8</artifactId><version> 1.2.3</version></dependency><dependency><groupId> junit</groupId><artifactId> junit</artifactId><version> 4.12</version></dependency><dependency><groupId> info.cukes</groupId><artifactId> cucumber-junit</artifactId><version> 1.2.4</version></dependency></dependencies>

Compared with the first part, added cucumber-junit – startup library Junit tests with Cucumber There is one for at least TestNG

Package added runners It contains the class SmokeTest , which is perceived by Junit -as a test that will be run by the mvn test Let’s take a closer look at this class

@RunWith(Cucumber.class)@CucumberOptions(plugin= {"html:target/cucumber-report/smoketest", "json:target/cucumber.json"}, features= "src/test/java/ru/riskmarket/features", glue= "ru/riskmarket/steps", tags= "@smoketest")public class SmokeTest{@BeforeClassstatic public void setupTimeout(){Configuration.timeout = 10000;}}

@RunWith – annotation that is responsible for running this JUnit test along with Cucumber

@CucumberOptions – Here we configure our Cucumber test

plugin – is responsible for creating test reports (formerly called format ). About reports later

features – path to features

glue – path to steppes

tags – features and even individual scripts can be tagged; this parameter indicates which tests will be run.

Tags are written in line, separated by commas : "@smoketest, @alltests, @special" For example, if you have several features, you can add @alltests and then a runner with the parameter tags = "@alltests" run all tests.

Tags are added to features before the keyword Features: after a space or on a new line

You can leave the body of the class empty, but since I run tests with a slow internet connection, I will set a selenium timeout here – I do it through JUnit’s @BeforeClass , I specify 10s vs default 4s. You can also specify in the configuration, e.g. under which browser you run the tests

System.setProperty("webdriver.chrome.driver", "src/main/resources/chromedriver.exe");Configuration.browser = "chrome";

If you decide to change your browser, don’t forget to download the driver you need from Selenium and specify its location. Now, as an example, I leave the lines in the runner commented out.

Steps in the fic smoketest are the same as in the fiche from part 1, but they are written in a slightly different way because now we use PageObject

PageObject – is a pattern that implies that there are classes of individual pagesthat contain definitions of items needed for tests, with selectors specified. The stack logic then uses the previously defined elements rather than the selectors. This is needed in case the html structure of the project changes. If there is a change, only the selectors in PageObject and you don’t have to search through all StepDefinition which selectors are now working and which are not.

There are three pages in our test that are listed in the package ru.riskmarket.pageobjects It is accepted to write pageobjects in main/java (in the sorts), because pages – are not the tests themselves. Each page specifies the elements that belong to it, with which the test interacts.

Adding your annotation

Now let’s imagine we have a huge project with several pages with hundreds of elements, dynamic tables, something else, in general, a complex project. In this case, elements of this type appear – "first cell of the third column in the matching table" Given the desire to overuse stacks, we need to somehow pass the name of the element to the method that defines the logic of the stack. Our complex element in PageObject will be defined as

@findBy(xpath = "selector")public SelenideElement firstCellAtThirdRowAtAssessmentTable;

And then a step writing something to this cell could look like this :

And type to firstCellAtThirdRowAtAssessmentTable text: "Hello, Cucumber!"

As you can see, you lose the advantage of making the steppes understandable. I wish the step looked like this :

And type to “First Cell At Third Row At Assessment Table” text: "Hello, Cucumber!"

Then you have to somehow in the method of steppe from StepDef match the element with its name from the steppe itself.
My solution is to add the annotation @NameOfElement.

I won’t parse the abstract, that’s a separate topic. The main thing is that it’s easy enough to write them.

As a result, after adding the annotation, you get a double work: instead of referring directly to the element, especially since its name is known, it is done through reflection and annotation. But this is a sacrifice for beautiful, readable features.

As a result, our element will be defined as :

@NameOfElement("First Cell At Third Row At Assessment Table")@findBy(xpath = "selector")public SelenideElement firstCellAtThirdRowAtAssessmentTable;

And in StepDef :

@And("^type to input \"([^\"]*)\" text: \"([^\"]*)\"$")public void typeToInputText(String nameOfElement, String text){somePage.get(nameOfElement).sendKeys(text);}

Method get(nameOfElement) uses reflexion to find the field in the page class by name, and then it triggers @FindBy from Selenium and returns an item on the page.

Method get(nameOfElement) is defined in the class AbstractPage.java , which is inherited by all pages.

Talk about changes in MyStepDef.

Fields added

FirstPage firstPage = page(FirstPage.class);SecondPage secondPage = page(SecondPage.class);ThirdPage thirdPage = page(ThirdPage.class);

page(PageObject.class) – this method Selenide , which encapsulates the Selenium PageFactory Again a significant reduction in the code. This is necessary in order to work out @FindBy when searching for an element on the page.

In the stapes methods, unlike in the first draft, the selenides $(selector) have been replaced by a search for an element in the desired page by its name. In stacks common to all pages, the element is searched across all pages.

Read more about should(Condition)

In its essence, the Selenide condition check should/shouldBe/shoudHave/.... (the methods do the same thing but are named differently to make it easier to read), are analogous to asserts , with the difference that if you use asserts , then you have to take care of creating screenshots of the fallen aserts. Again extra code that the tester who comes to your place will have to take apart. And in the case of Cucumber You still have a lot of work to do.

should(Condition) automatically takes a screenshot when it crashes and the report will include a link to the screenshot. By default, they are saved in the project folder build

Log clipping :

Element not found {By.xpath: //button[.='Entrance to the cabinet']}Expected: visibleScreenshot: file:/C:/Users/vkov/Documents/GitHub/RiskMarket/complex_selenide_cucumber/build/reports/tests/1460907962193.0.png

As you can see, the logging is analogous to assertThat – it logs what should have happened and what actually happened.

For most cases the one you need is already written Condition , look it up before you use assertThat As a last resort, if the desired Condition is absent and it’s necessary to make a screenshot, you can create your own one.A simple example of this is in the class CustomCondition It repeats the action that performs Condition.appear

For should() you can add your own description, which is logged by :

somePage.get("Element name").shouldBe(Condition.visible.because("Because..."))

In case of Cucumber , I think this is unnecessary, because you are exactly repeating the text you have describing the step where this check occurs.

For collections of elements are also written should() An example from the project :

View in fiche :

Then collection of "Search results" should not be empty

View in MyStepdefs:

@Then("^collection of \"([^\"]*)\" should not be empty$")public void collectionOfShouldNotBeEmpty(String collection){ElementsCollection selenideElements = secondPage.getCollection(collection);selenideElements.shouldHave(CollectionCondition.sizeGreaterThan(0));}

Explanations are unnecessary, everything is clear from the code.

Let’s talk about reports

Back to the runner class `SmokeTest.java’

@RunWith(Cucumber.class)@CucumberOptions(plugin= {"html:target/cucumber-report/smoketest", "json:target/cucumber.json"}, features = "src/test/java/ru/riskmarket/features", glue = "ru/riskmarket/steps", tags = "@smoketest")public class SmokeTest{@BeforeClassstatic public void setupTimeout(){Configuration.timeout = 10000;}}

If not specified in @CucumberOptions plugin , no report will be generated.

plugin = {"html:target/cucumber-report/smoketest"} generates reports like this :

UI tests:Cucumber+ Selenide

Green indicates steppes that have succeeded, red indicates that they have failed, and turquoise indicates that they have not started. There is also yellow, which means that the step is not defined, a reminder for you. On the red background you can find a link to the screenshot and everything else StackTrace

These reports are good, easy to read, and you can copy from them into steps-to-reproduce when you start a bug.

But there are even better ones.
From here : https://github.com/damianszczepanik/maven-cucumber-reporting

Add to pom.xml

<build><plugins><plugin><groupId> org.apache.maven.plugins</groupId><artifactId> maven-surefire-plugin</artifactId><configuration><testFailureIgnore> true</testFailureIgnore></configuration></plugin><plugin><groupId> net.masterthought</groupId><artifactId> maven-cucumber-reporting</artifactId><version> 2.0.0</version><executions><execution><id> execution</id><phase> verify</phase><goals><goal> generate</goal></goals><configuration><projectName> cucumber-selenide-example</projectName><outputDirectory> target/cucumber-html-reports</outputDirectory><cucumberOutput> target/cucumber.json</cucumberOutput><parallelTesting> false</parallelTesting></configuration></execution></executions></plugin></plugins></build>

In @CucumberOptions specify plugin = {"json:target/cucumber.json"} This plugin builds the report exactly from the .json – report.

Line <cucumberOutput> target/cucumber.json</cucumberOutput> – Configures where to take the .json report from

Line <outputDirectory>target/cucumber-html-reports</outputDirectory> – configures where the report will be stored.

Module works only through mvn clean install After execution in target/cucumber-html-reports open feature-overview.html and get these things :

UI tests:Cucumber+ Selenide
UI tests:Cucumber+ Selenide
UI tests:Cucumber+ Selenide
UI tests:Cucumber+ Selenide

In tab Steps (last screenshot) time estimates for step analysis for refactoring.

Actually the module was written for CI Jenkins How to get these reports in Jenkins see here : https://github.com/damianszczepanik/cucumber-reporting

It is not configured for Bamboo, so you can use the module described in this article and just specify the location of the report as an artifact.

That’s all for now. Understandable, no-drop tests to you!

P.S. This article didn’t include the use of parameterized stacks (Scenario Outline:, Examples: ) if you’re thinking of using Cucumber in your own place, read about it here

You may also like