Using BDD and Cucumber in Squash AUTOM - Automating test cases
Getting the test cases
Annita Squash TM administrator |
Pravash Product owner |
Fabrice Functional tester |
Antonine Automatician |
---|---|---|---|
Set up Squash TM | |||
Write requirements | |||
Write test cases | |||
Transmit test cases | |||
Get test cases | |||
Automate test cases | |||
Deliver automated test cases | |||
Indicate automation is performed | |||
Run automated tests |
Picking up the test cases to automate in Squash TM
Antonine picks some tests to be automated in the "To Do" Tab of the Automation Workspace (see Squash TM documentation) by selecting them and clicking the "Assign to me" button.
Then, using "Assigned to me" Tab of the Automation Workspace (see Squash TM documentation), she indicates that the automation of these tests is started:
Getting the feature
files
Antonine updates her local repository to get the feature
files pushed by Squash TM in the remote Git repository.
git pull
Automating the test cases
Annita Squash TM administrator |
Pravash Product owner |
Fabrice Functional tester |
Antonine Automatician |
---|---|---|---|
Set up Squash TM | |||
Write requirements | |||
Write test cases | |||
Transmit test cases | |||
Get test cases | |||
Automate test cases | |||
Deliver automated test cases | |||
Indicate automation is performed | |||
Run automated tests |
Boiler plate setup
Before implementing the first Cucumber steps, Antonine needs to properly set up her Maven project.
.gitignore
file
Antonine creates the usual .gitignore
file.
The file contains:
target/
pom.xml
file
She creates the following pom.xml
file to use Cucumber, JUnit, and Selenium:
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>prestashoptest</groupId>
<artifactId>prestashoptest</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.2.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>1.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>7.2.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.1.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<encoding>UTF-8</encoding>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
</plugins>
</build>
</project>
src/test/resources/cucumber.properties
file
She also creates the src/test/resources/cucumber.properties
file in order to avoid Cucumber messages about report publication.
The file contains:
cucumber.publish.quiet=true
cucumber.publish.enabled=false
Configuration of Cucumber in RunCucumberTest.java
file
Antonine creates the src/test/java/prestashoptest/RunCucumberTest.java
root test file
package prestashoptest;
import org.junit.runner.RunWith;
import io.cucumber.junit.Cucumber;
@RunWith(Cucumber.class)
public class RunCucumberTest {
}
Structure of the code base
Antonine creates some directories in src/test/java/prestashoptest
to organize her code.
The resulting file hierarchy is:
-
.git
/ → Git -
src
/-
test
/-
java
/-
prestashoptest
/datatypes
/ → data types required to model the business objectshelpers
/ → technical helpers (StringHelper
…)pageobjects
/ → page objectsAccountCreationPageObject.java
CartPageObject.java
CatalogPageObject.java
HomePageObject.java
IdentityPageObject.java
ProductPageObject.java
SignInPageObject.java
seleniumtools
/ → Selenium tools (helpers for Selenium usage)stepdefinitions
/ → definition of the Cucumber stepsAccountStepDefinitions.java
CartStepDefinitions.java
CommonStepDefinitions.java
RunCucumberTest.java
SetupTeardown.java
-
-
resources
/-
prestashoptest
/ →feature
files generated by Squash TM 🛑 These files must not be modified!-
account
431_Create_an_account__then_check_login_and_personal_data.feature
432_Create_an_account__then_try_to_login_with_incorrect_password.feature
- …
-
cart
240_Add_one_product_in_the_cart.feature
303_Add_two_products_in_the_cart.feature
304_Add_twice_a_product_to_the_cart.feature
- …
-
-
cucumber.properties
→ configuration of Cucumber
-
-
-
-
target
/ → files generated by Mavencucumber-reports
/ → test reports
-
.gitignore
pom.xml
Some design notes
Antonine wants to clearly separate the concerns:
src/test/java/prestashoptest/seleniumtools
- contains most of the code related to interface with WebDriver
- is a technical layer
it does not know about the business rules
-
src/test/java/prestashoptest/pageobjects
contains the usual page objects.
A page object is a Java class encapsulating the technical details of a tested page (IDs, names, XPaths… of the HTML elements).
This design pattern has two main advantages:- It reduces the maintenance cost by centralizing the page details in one place: if a developper modifies a HTML element on the page and this change breaks the locator used by Antonine to find the element, she would have to update it in only one place.
- It provides a semantic API: the code using the page object knows about the meaning of the page, but it does not need to know anything about the HTML structure or about the JavaScript behavior of the page.
The page objects are using WebDriver mostly via the
seleniumtools
classes (e.g. for all the basic operations such as setting a field value, getting a field value, clicking on an element…). In some few cases, when a page object needs to perform some operations which are too specific to be in aseleniumtools
class, it calls WebDriver directly. -
src/test/java/prestashoptest/stepdefinitions
contains the implementation of the Cucumber steps.
Each Cucumber step interacts with some page objects. It has no knowledge about WebDriver, HTML, JavaScript…
The src/test/resources/prestashoptest
directory
🛑 The content of the src/test/resources/prestashoptest
directory must not be modified, it is controlled by Squash TM.
Modifying some files in that directory could impede the transmission of new or updated test cases (i.e. feature
files) in the future.
But, even if the transmission would work, all the modifications would be lost, overwritten by Squash TM.
Writing setup and teardown
Once the Maven project has been set up, Antonine implements some necessary basic building bricks, starting with the setup and teardown of the tests that she places in src/test/java/prestashoptest/SetupTeardown.java
.
-
the setup of each test:
This setup initializes the Browser type and URL of the tested application.@Before public void setup() { PageObjectBase.setBrowser(PageObjectBase.Browser.FIREFOX); PageObjectBase.setHost("http://localhost:8080/fr"); }
-
the teardown of each test:
This teardown generates a screenshot and transmits it to Cucumber in case of a test failure. This will be very helpful to analyse why the failure occured.@After public void closeBrowser(final Scenario scenario) { if (scenario.isFailed()) { final byte[] screenshot = PageObjectBase.getScreenshot(); final String screenshotName = "screenshot " + scenario.getName() + " (line " + scenario.getLine() + ")"; scenario.attach(screenshot,"image/png", screenshotName); } PageObjectBase.quit(); }
The teardown also quits the WebDriver session.
Writing Selenium tools
Antonine uses inheritance to make the Selenium tools easy to call: src/test/java/prestashoptest/seleniumtools/PageObjectBase.java
is the base class for all page objects.
It contains methods to
-
fill a field and get its value
protected void fillFieldValue(final SelectBy by, final String fieldKey, final String value) { final WebElement element = by.findElement(getDriver(), fieldKey); element.clear(); element.sendKeys(value); } protected String getFieldValue(final SelectBy by, final String fieldKey) { return by.findElement(getDriver(), fieldKey).getAttribute("value"); }
-
click on an element
protected void clickElement(final SelectBy by, final String elementKey) { by.findElement(getDriver(), fieldKey).click(); }
-
get the status of a checkbox
protected boolean isCheckBoxSelected(final SelectBy by, final String checkBoxKey) { return by.findElement(getDriver(), checkBoxKey).isSelected(); }
-
and so on…
It also contains some static methods to declare the host where the SUT is deployed, to select the browser to use for the test, to generate a screenshot, to quit the WebDriver session… (These methods are mostly used during the setup and teardown of the tests described above.)
public static void setHost(final String host) {
PageObjectBase.host = host;
}
public static void setBrowser(final Browser browser) {
PageObjectBase.browser = browser;
}
public static void quit() {
PageObjectBase.browser.quit();
}
public static byte[] getScreenshot() {
return ((TakesScreenshot)getDriver()).getScreenshotAs(OutputType.BYTES);
}
Writing page objects
Once these foundation bricks are implemented, Antonines creates a page object for each page with which a Cucumber step will interact.
Most page objects are very simple and only use the PageObjectBase
's methods.
For example, for the account creation page:
the page object (src/test/java/prestashoptest/pageobjects/AccountCreationPageObject.java
file) is:
package prestashoptest.pageobjects;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import prestashoptest.datatypes.Gender;
import prestashoptest.seleniumtools.PageObjectBase;
/**
* Account creation page
*/
public class AccountCreationPageObject extends PageObjectBase {
public AccountCreationPageObject() {
super("connexion?create_account=");
}
/**
* Fill the form with the specified values (default values are used for the other fields)
* and initiate the account creation.
*
* @param gender
* @param firstName
* @param lastName
* @param password
* @param email
* @param birthDate
* @param acceptPartnerOffers
* @param acceptPrivacyPolicy
* @param acceptNewsletter
* @param acceptGdpr
*/
public void fillNewAccountFields(final Gender gender,
final String firstName,
final String lastName,
final String password,
final String email,
final LocalDate birthDate,
final boolean acceptPartnerOffers,
final boolean acceptPrivacyPolicy,
final boolean acceptNewsletter,
final boolean acceptGdpr) {
fillGender(gender);
fillFirstName(firstName);
fillLastName(lastName);
fillEmail(email);
fillPassword(password);
fillBirthDate(birthDate);
if (acceptPartnerOffers) acceptPartnerOffers();
if (acceptPrivacyPolicy) acceptPrivacyPolicy();
if (acceptNewsletter) acceptNewsletter();
if (acceptGdpr) acceptGdpr();
submitNewAccountForm();
}
/**
* Fill the gender field
* If the gender is undefined, the field is untouched.
*
* @param gender
*/
public void fillGender(final Gender gender) {
if (gender.equals(Gender.UNDEFINED)) {
return;
}
final String id = (gender.equals(Gender.MALE)) ? "field-id_gender-1"
: "field-id_gender-2";
clickElement(SelectBy.ID, id);
}
/**
* Fill the first name field
*
* @param firstName
*/
public void fillFirstName(final String firstName) {
fillFieldValue(SelectBy.ID, "field-firstname", firstName);
}
/**
* Fill the last name field
*
* @param lastName
*/
public void fillLastName(final String lastName) {
fillFieldValue(SelectBy.ID, "field-lastname", lastName);
}
/**
* Fill the email field
*
* @param email
*/
public void fillEmail(final String email) {
fillFieldValue(SelectBy.ID, "field-email", email);
}
/**
* Fill the password field
*
* @param password
*/
public void fillPassword(final String password) {
fillFieldValue(SelectBy.ID, "field-password", password);
}
/**
* Fill the password field
*
* @param birthDate
*/
public void fillBirthDate(final LocalDate birthDate) {
fillFieldValue(SelectBy.ID, "field-birthday", birthDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
}
/**
* Approve for partner offers
*/
public void acceptPartnerOffers() {
clickElement(SelectBy.NAME, "optin");
}
/**
* Approve the customer privacy policy
*/
public void acceptPrivacyPolicy() {
clickElement(SelectBy.NAME, "customer_privacy");
}
/**
* Sign up for the newsletter
*/
public void acceptNewsletter() {
clickElement(SelectBy.NAME, "newsletter");
}
/**
* Approve the GDPR policy
*/
public void acceptGdpr() {
clickElement(SelectBy.NAME, "psgdpr");
}
/**
* Initiate the account creation
*/
private void submitNewAccountForm() {
clickElement(SelectBy.XPATH, "//*[@id=\"customer-form\"]/footer/button");
}
}
In a few cases, the HTML is more difficult to navigate, so the page object is more complex.
For example, to retrieve the content of the cart:
the page object (src/test/java/prestashoptest/pageobjects/CartPageObject.java
file) is:
package prestashoptest.pageobjects;
import java.util.ArrayList;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import prestashoptest.datatypes.CartLine;
import prestashoptest.seleniumtools.PageObjectBase;
public class CartPageObject extends PageObjectBase {
public CartPageObject() {
super("panier?action=show");
}
public List<CartLine> getContent() {
final List<WebElement> items = getElements(SelectBy.CLASS, "cart-item");
final List<CartLine> list = new ArrayList<CartLine>(items.size());
for (final WebElement item: items) {
final WebElement productElem = item.findElement(By.className("label"));
final String product = productElem.getText();
final WebElement quantityElem = item.findElement(By.name("product-quantity-spin"));
final int quantity = Integer.parseInt(quantityElem.getAttribute("value"));
final List<WebElement> dimensionElem = item.findElements(By.className("dimension"));
final String dimension = dimensionElem.isEmpty() ? null : dimensionElem.get(0).findElement(By.className("value")).getText();
final List<WebElement> sizeElem = item.findElements(By.className("taille"));
final String size = sizeElem.isEmpty() ? null : sizeElem.get(0).findElement(By.className("value")).getText();
final List<WebElement> colorElem = item.findElements(By.className("couleur"));
final String color = colorElem.isEmpty() ? null : colorElem.get(0).findElement(By.className("value")).getText();
list.add(new CartLine(product, quantity, dimension, size, color));
}
return list;
}
}
Implementing Cucumber steps
The src/test/java/prestashoptest/stepdefinitions/*.java
files contain the definitions of the Cucumber steps.
Antonine groups the steps related to a given feature domain in the same class. So she creates AccountStepDefinitions.java
, CartStepDefinitions.java
… At last, CommonStepDefinitions.java
contains transverse steps.
There are three types of steps:
Given
A Given
step is used to set up a test precondition. So it may navigate and interact with several pages in order to arrange the necessary data.
For example, some test cases need the (unspecified) user to be logged in. So they use the Given I am logged in
step.
This step is implemented by using the account creation page and creating a one-time user (src/test/java/prestashoptest/stepdefinitions/AccountStepDefinitions.java
file):
package prestashoptest.stepdefinitions;
…
import io.cucumber.java.en.Given;
…
public class AccountStepDefinitions {
/**
* Create a temporary user and keep him/her logged in
*/
@Given("I am logged in")
public void createTemporaryUser() {
final String id = StringsHelper.generateRandomId();
final AccountCreationPageObject newAccountPage = new AccountCreationPageObject();
newAccountPage.goTo();
newAccountPage.fillNewAccountFields(Gender.UNDEFINED,
"first. " + id,
"last. " + id,
id,
id + "@example.com",
LocalDate.of(2000, 1, 1),
true,
true,
true,
true);
final HomePageObject homePage = new HomePageObject();
homePage.assertIsCurrent();
}
…
}
When
A When
step performs an action.
For example, When I am on the AccountCreation page
is implemented (in the same file) by:
/**
* Go to the Account Creation page
*/
@When("I am on the AccountCreation page")
public void displayNewAccountPage() {
final AccountCreationPageObject newAccountPage = new AccountCreationPageObject();
newAccountPage.goTo();
}
Many When
steps expect to be applied while a given page is displayed, so they assert that this is the case.
For example, the When("I fill AccountCreation fields with gender {string} firstName {string} lastName {string} password {string} email {string} birthDate {string} acceptPartnerOffers {string} acceptPrivacyPolicy {string} acceptNewsletter {string} acceptGdpr {string} and submit
step is implemented by:
/**
* Fill the fields of the Account Creation page and submit the form
*
* The current page must be the Account Creation page
*/
@When("I fill AccountCreation fields with gender {string} firstName {string} lastName {string} password {string} " +
"email {string} birthDate {string} acceptPartnerOffers {string} acceptPrivacyPolicy {string} acceptNewsletter{string} acceptGdpr {string} and submit")
public void fillAccountCreationFieldsAndSubmit(final String genderCode,
final String firstName,
final String lastName,
final String password,
final String email,
final String birthDate,
final String acceptPartnerOffers,
final String acceptPrivacyPolicy,
final String acceptNewsletter,
final String acceptGdpr) {
final AccountCreationPageObject newAccountPage = new AccountCreationPageObject();
newAccountPage.assertIsCurrent();
newAccountPage.fillNewAccountFields(Gender.ofCode(genderCode),
firstName,
lastName,
password,
email,
LocalDate.parse(birthDate),
StringsHelper.convertYesNoIntoBoolean(acceptPartnerOffers),
StringsHelper.convertYesNoIntoBoolean(acceptPrivacyPolicy),
StringsHelper.convertYesNoIntoBoolean(acceptNewsletter),
StringsHelper.convertYesNoIntoBoolean(acceptGdpr));
}
Then
A Then
step is used to set up a test postcondition. So it may navigate and interact with several pages in order to check that the data is the expected one.
For example, the Then My personal information is gender {string} firstName {string} lastName {string} email {string} birthDate {string} acceptPartnerOffers {string} acceptPrivacyPolicy {string} acceptNewsletter {string} acceptGdpr {string}
step is implemented by displaying the personal data page, getting the field values, and checking that they have the expected values:
/**
* Verify that the personal data is equal to the given parameters
*/
@Then("My personal information is gender {string} firstName {string} lastName {string} " +
"email {string} birthDate {string} acceptPartnerOffers {string} acceptPrivacyPolicy {string} acceptNewsletter {string} acceptGdpr {string}")
public void assertPersonalInformation(final String genderCode,
final String firstName,
final String lastName,
final String email,
final String birthDate,
final String acceptPartnerOffers,
final String acceptPrivacyPolicy,
final String acceptNewsletter,
final String acceptGdpr) {
final IdentityPageObject identityPage = new IdentityPageObject();
identityPage.goTo();
Assertions.assertEquals(Gender.ofCode(genderCode),
identityPage.getGender(),
"The effective gender is not the expected one");
Assertions.assertEquals(firstName,
identityPage.getFirstName(),
"The effective first name is not the expected one");
Assertions.assertEquals(lastName,
identityPage.getLastName(),
"The effective last name is not the expected one");
Assertions.assertEquals(email,
identityPage.getEmail(),
"The effective email is not the expected one");
Assertions.assertEquals(LocalDate.parse(birthDate),
identityPage.getBirthDate(),
"The effective birth date is not the expected one");
Assertions.assertEquals(StringsHelper.convertYesNoIntoBoolean(acceptPartnerOffers),
identityPage.doesAcceptPartnerOffers(),
"The effective acceptPartnerOffers is not the expected one");
Assertions.assertEquals(StringsHelper.convertYesNoIntoBoolean(acceptPrivacyPolicy),
identityPage.doesAcceptPrivacyPolicy(),
"The effective acceptPrivacyPolicy is not the expected one");
Assertions.assertEquals(StringsHelper.convertYesNoIntoBoolean(acceptNewsletter),
identityPage.doesAcceptNewsletter(),
"The effective acceptNewsletter is not the expected one");
Assertions.assertEquals(StringsHelper.convertYesNoIntoBoolean(acceptGdpr),
identityPage.doesAcceptGdpr(),
"The effective acceptGdpr is not the expected one");
}
Some implementation details
Regular expressions vs. Cucumber expressions
There are two ways to write the patterns matching the parameters of the Gherkin steps:
-
the usual Java regular expressions:
public class CartStepDefinitions { @Then("I have (\\d+) products in my cart") public void assertNumberOfProductsInCart(final String argNbProducts) { int nbProducts = Integer.parseInt(argNbProducts); System.out.format("Products: %n\n", nbProducts); } }
-
the Cucumber expressions:
They allow to quickly match common data types (public class CartStepDefinitions { @Then("I have {int} products in my cart") public void assertNumberOfProductsInCart(final int nbProducts) { System.out.format("Products: %n\n", nbProducts); } }
string
,word
,int
,float
…) without having to write the boiler code for converting text into integers, floats… So Antonine uses these ones.
Lambdas vs. classic methods
There are two ways to implement Gherkin steps:
-
using classic methods:
For this, the following dependency must be added in thepackage prestashoptest.stepdefinitions; import io.cucumber.java.en.Then; public class CartStepDefinitions { @Then("I have {int} products in my cart") public void assertNumberOfProductsInCart(final int nbProducts) { System.out.format("Products: %n\n", nbProducts); } }
pom.xml
file:<dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-java</artifactId> <version>7.2.3</version> <scope>test</scope> </dependency>
-
using lambdas:
For this, the following dependency must be added in thepackage prestashoptest.stepdefinitions; import io.cucumber.java8.En; public class CartStepDefinitions implements En { public CartStepDefinitions() { Then("I have {int} products in my cart", (final Integer nbProducts) -> { System.out.format("Products: %n\n", nbProducts); }); } }
pom.xml
file:<dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-java8</artifactId> <version>7.2.3</version> <scope>test</scope> </dependency>
The second method requires less code, there is no need to define the useless assertNumberOfProductsInCart
method name. But Cucumber supports lambdas only up to 9 parameters in a given Gherkin step.
Fabrice and Antonine agreed to have a single step to fill a given application form in order to keep the steps simple for Fabrice. But the account creation form has 10 parameters, so Antonine sticks with the classic methods.
Use Maven to generate the code skeleton
In order to save some time for implementing the Gherkin steps, Antonine uses Maven to generate the code skeleton.
She launches the test:
mvn clean test -Dcucumber.features=src/test/resources/prestashoptest/Account_management/431_Create_an_account__then_check_login_and_personal_data.feature
[ERROR] The step 'I fill AccountCreation fields with gender "M" firstName "John" lastName "Doe" password "mypassword" email "jdoe@example.com" birthDate "1990-01-02" acceptPartnerOffers "no" acceptPrivacyPolicy "yes" acceptNewsletter "yes" acceptGdpr "yes" and submit' is undefined.
You can implement this step using the snippet(s) below:
@When("I fill AccountCreation fields with gender {string} firstName {string} lastName {string} password {string} email {string} birthDate {string} acceptPartnerOffers {string} acceptPrivacyPolicy {string} acceptNewsletter {string} acceptGdpr {string} and submit")
public void i_fill_account_creation_fields_with_gender_first_name_last_name_password_email_birth_date_accept_partner_offers_accept_privacy_policy_accept_newsletter_accept_gdpr_and_submit(String string, String string2, String string3, String string4, String string5, String string6, String string7, String string8, String string9, String string10) {
// Write code here that turns the phrase above into concrete actions
throw new io.cucumber.java.PendingException();
}
cucumber-java8
is used, the proposed skeletons use lambdas.)
Delivering the automated test cases
Annita Squash TM administrator |
Pravash Product owner |
Fabrice Functional tester |
Antonine Automatician |
---|---|---|---|
Set up Squash TM | |||
Write requirements | |||
Write test cases | |||
Transmit test cases | |||
Get test cases | |||
Automate test cases | |||
Deliver automated test cases | |||
Indicate automation is performed | |||
Run automated tests |
Once Antonine has implemented all the Cucumber steps used by the features
files, she checks that the tests work properly by running them:
mvn test -Dcucumber.plugin="html:target/cucumber-reports/Cucumber_html.html"
target/cucumber-reports/Cucumber_html.html
file):
When she finds something suspicious while automating a test, she performs the test manually and, if there really is a bug, she reports it as described in the Squash TM documentation.
When the tests are running properly, she commits and pushes:
git add .
git commit -m "Implemented first account and cart steps"
git push
Indicating that the automation is performed
Annita Squash TM administrator |
Pravash Product owner |
Fabrice Functional tester |
Antonine Automatician |
---|---|---|---|
Set up Squash TM | |||
Write requirements | |||
Write test cases | |||
Transmit test cases | |||
Get test cases | |||
Automate test cases | |||
Deliver automated test cases | |||
Indicate automation is performed | |||
Run automated tests |
Then, using the "Assigned to me" Tab of the Automation Workspace (see Squash TM documentation), Antonine indicates that the tests have been automated:
She also sets the technology of the tests: (This setting should be automatically done by Squash TM, this will be fixed in a future release.)