Test, Basic Information - epam/Gepard GitHub Wiki

Tested Application

Effective test automation requires preparation of the tested applications (or components or services). For example in case of a tested Web application, the HTML content should provide suitable information to the test script writer. See UI Code Convention for Web Applications on how to make the web pages test friendly.

Coding rules

  • Test Automation code must follow the coding (including checkstlye) rules those are applied to the project itself.

Exceptions:

The usage of the supression.xml is allowed only when the fix of the checkstyle error needs huge code refactor. Even in this case use the following approach to turn off checkstyle:

//CHECKSTYLE.OFF
code that intentionally violates a rule
//CHECKSTYLE.ON

Note: Using System.out.println is prohibited! Use logComment/logStep/logEvent or do use Debug feature of the IDE :)

General conventions

  • Arrange the test classes in a package hierarchy if there are several of them. This allows you to specify better filter expressions, for example to run all tests which create preconditions, or all mail-related tests, etc.
  • Before starting the implementation, be sure that you are able to perform the Test Case manually (or can debug it).
  • When a Test Case is modified (Requirement is changed or the Test Case is altered), existing Test Case Implementations may become unusable. (Modified TCs should be handled as brand new TCs -> check whether it can be implemented and implement it again).
  • Commit/Submit your work frequently.
  • Never commit anything into build/result folder
  • Never commit log files

Suggested Package Structure

Common package structure

Some folders are created to collect common features and utility classes. The structure is the following:

Source:

  • com.<company>.<product>.common.controller -> stores all functions for a specific module/view

  • com.<company>.<product>.common.database -> stores all database query wrapper methods for different entities and dtos

  • com.<company>.<product>.common.fwextension -> stores extensions for the Gepard framework

  • com.<company>.<product>.common.handler -> stores util or handler classes those are common for the product

  • com.<company>.<product>.common.model -> stores data objects in hierarchy which are available to store the data value of the fields to be filled on the pages

  • com.<company>.<product>.common.seleniumext -> stores classes to extend the standard selenium (if applicable)

  • com.<company>.<product>.common.view -> stores locator subpackages (V from MVC, locators of UI modules which are appearing on the specified pages, modules)

  • com.<company>.<product>.common.validator -> stores common page checks. Code that actually tests the page should be in validators.

  • com.<company>.<product>.common.feeders -> stores common data parameters that are used in more tests

    Resources:

  • com.<company>.<product>.common.database -> stores XML mappings for database queries for different entitites

  • com.<company>.<product>.common.handler.database -> stores SQL map config properties

    **Please keep in mind that these are the suggested packages. **

Team package structure

Every (sub)team is suggested to have a dedicated package structure. Team specific objects should be placed in this area. The team shall maintain the tests of other teams as part of the regression! The suggested structure is the following:

  • com.<company>.<product>.test.<teamName>.test -> to store the test classes. Test data should be placed in the same package structure under resources folder.
  • com.<company>.<product>.test.<teamName>.handler -> to store the project specific handler/utility code
  • com.<company>.<product>.test.<teamName>.model -> to store data objects in hierarchy which are available to store the data value of the fields to be filled on the pages
  • com.<company>.<product>.test.<teamName>.view -> to store locator subpackages (V from MVC)
  • com.<company>.<product>.test.<teamName>.controller -> to store all functions for a specific module/view

should be agreed between the team within the project.

Import

Using <package>.* imports within your test case and your helpers is forbidden.

Gepard Extension

Every test case should implement the following interface:

  • com.epam.gepard.generic.GepardTestCase

All of the methods of this interface has default body, that means you don't need to overwrite/implement anything. See example implementations in gepard-examples module. 

Note for Gepard V3.x: All test cases should extend com.epam.gepard.generic.CommonTestCase class. This is not applicable for Gepard V4.x.

Naming Conventions

See [Naming Conventions] page.

Verification points in test methods

There are two types of methods for checking if the actual result is what we expect:

  • assert methods - compares actual and expected result and continues with the next statement if it is true, exits from the test method with failed result otherwise,

  • verify methods - compares actual and expected result, if it is false the test case becomes failed, but continues with the next statement anyways.

Note: Never use assertXXX(check) methods, because this will not give info about the problem, just the TC fails. Always use the assertXXX(errorText, check) methods, where the problem should be described in the errorText field.

Comments

Framework comments

Besides others framework provides to methods to be called in case of generating comments into the test execution report:

Logging must be done through the GepardTestClass interface..

  • logComment("Comment") if someone would like to add a comment line into the report.
  • logComment("Comment", "Description") Also comments, but in the HTML report under the description link, your given description will be shown.
  • logStep("Step description") if a normal step or verification point is coming in the test code.
  • logEvent("Comment") if you want to log something important, a milestone in your test.
  • logWarning() logs a comment to the report with purple color.

Please call the logStep prior to the assertion or step to be done, like:

logStep("Go to homepage");
openUrl(url);

In-line comments

Please write as many in-line comments as possible especially in a handler class. It is extremely important when you or the colleague the code is handed over is trying to understand your code any time in future. The recommended percentage of comments compared to the LOC is minimum 40%.

JavaDoc

Class

Write JavaDoc comment to each and every test or handler/util classes. The author tag has high importance as that person is the responsible for that specific class.

Method

Create a JavaDoc with throws, parameter description, short summary of the method. Use HTML coding conventions in order to add special style into your JavaDoc documentation.

Oracle recommendation on JavaDoc writing

Private methods

It may happen that checkstyle does not require to write JavaDoc to your private methods, but people does. If you write a test and put some common logic to a private method, you must write JavaDoc and explain the logic there. So if someone in the future have to refactor your code, it will be clear for him/her what is exactly the purpose and structure of that method.

Test Classes

  • Test classes should be annotated with the @TestClass annotation. 'id' and 'name' parameters are mandatory.
  • The only mandatory thing what a TestClass should hold, is a test method (annotated with @Test). Note that such test method may be hidden in any ancestor Test Class.

Example:

@TestClass(id = "TEST_PRODUCT_TC", name = "Example to Test X Feature")
public class ExampleTest implements GepardTestClass {

    @Test
    public void example() {
    }  

TestClass annotation parameters:

  • id - holds a unique identifier for the test.
  • name - hold a short description about the test.

Test Parameters

Java side:

  • Test parameters should be defined as private fields.
  • Test parameters may be initialized by using getDataDrivenTestParameter(by) method, where by can be integer (Nth column, starting from 0), or String (specifying the name of the column).
package example;

@TestClass(id = "TEST_PRODUCT_TC", name = "Example Data Driven Test")
public class ExampleTest implements GepardTestClass {

    private String first = getDataDrivenTestParameter(0);
    private String second = getDataDrivenTestParameter(1);
    private String third = getDataDrivenTestParameter(2);

    @Test
    public void test1() {
    }

}

Resource side:

  • Gepard uses txt files to define a list of tests to be executed and data files to use for the tests. The default test list file is testlist.txt.
  • The .csv (or .txt which is not preferred) file on the same path and with the same name as the test class is the default data feeder for the test.
  • In testlist.txt: fully.qualified.name.of.the.TestClass,[feeder]

Test Data Feeders

See more information on page: Test Data Feeders

Numbering / Indexing

  • It is recommended to use 0-based numbering at all places in the test code to avoid confusions. In Java, for example arrays and lists, the numbering starts with 0, however for xpath, it starts with 1. It's not very useful to combine these, so we support using the 0-based numbering at all places and make the adjustment where necessary.

Handler / Controller classes

Exception handling

JUnit has three end states of a test case passed, failed and failure. A test case is passed if the test found everything as expected, failed if something was not as expected or failure if the test could not be performed due to any reasons.

If the test method (test case) is using a handler performing a functionality which is not a step or verification point of the test case, if the handler can raise an exception we should do the followings:

  • catch the exception within the handler,
  • add an easy to understand message to it,
  • and throw it upwards.

A sample method in your handler should look like this:

public static void doSomething() throws MyException {
    //do something
    try {
        //do something which can raise an exception
    } catch (MyException e) {
        throw new MyException("Proper exception description!", e);
    }
}

This will raise and exception at the test method and the test result will be failure indicating that the test could not be performed.

Calling handlers

In case a handler or a controller need to create log entries into the test execution report we need to pass the current test case class. The call should be similar from the test case class (with static and void handler):

HandlerClass.handlerMethod(this);

The handler or controller should have the following implementation:

public static void handlerMethod(SeleniumTestCase stc) {
    stc.clickLink("");
    stc.selenium.click("");
    stc.selenium.getLocation();
}

As you can see from the above code part, after passing the test case itself, we have the possibility to call the built in methods or reach selenium and its own methods as well.

Queries and query handlers

If your test requires a special query to be executed, please create a separate SQL file into data subpackage which contains the query with $(sting) sign indicating the place of replacements. The following query gives back the destination id for a specific city and country:

SELECT d.DestinationIntID from DESTINATION d where d.Name = '$' and d.Country = '$'

Create a database query handler class which uses DatabaseHandler class from common and create wrapper methods for each query like the following:

public int getDestIDByCityAndCoutry(String city, String country){
    //call parameter replacer
    //execute the query
    //parse result
    return destID;
}

With this approach all SQL queries will have a method representation which can be seen easily with IDE's autocomplete function. These SQLs and their methods might be transferred to common area in order to be used by other components, projects.

Following MVC pattern

As the package structure shows we would like to follow the model, view, controller triplet in the test implementation.

Model: all data objects, but only the complicated ones like booking and user data. For simple data structure like newsletter sign in data do not over complicate with an object structure, use (CSV) wrappers instead.

View: try to use view hierarchy in case of similar modules (query pod), create a view for all different modules with inheritance. Those modules which are used on several pages should be under common and create page package for those which are unique.

Controller: write all default function into controllers for modules/views, like check existence, do something, etc. In case of complex modules use controllers which are referring to others. 

Simplicity in test classes

The test cases should be as simple as possible containing only logComment, logStep log calls, comment lines and helper or controller method calls. Try to move all logic to handlers or controllers as test cases should look like keyword driven code lines.

⚠️ **GitHub.com Fallback** ⚠️