How to make a basic test class for Selenium tests and initialize through JUnit RuleChain

With this article, we continue a series of publications on how we automated the process of manual testing (hereinafter referred to as auto-tests) of a large information system (hereinafter referred to as Systems) in one of the major LANIT projects and what came of it.

How to efficiently organize a class hierarchy? How to distribute packages on the project tree? How to make sure that you forget about merge conflicts with a team of 10 people? These issues are always at the start of a new development and they never have enough time.

A source

In this article, we describe the structure of classes and the organization of code, which allowed us to develop with little effort more than one and a half thousand end-2-end UI tests based on Junit and Selenium for a large system of federal significance. Moreover, we successfully support it and constantly refine existing scenarios.

Here you can find a practical description of the structure of the hierarchy of the base classes of autotests, the breakdown of the project according to the functional model java-packages and sample templates of real classes.

The article will be useful to all developers who develop self-tests based on Selenium.

This article is part of a general publication in which we described how a small team built the UI testing automation process and developed a framework based on Junit and Selenium for this.

Previous Parts:

  • Part 1. Organizational and managerial. Why did we need automation. Organization of the development and management process. Organization of use
  • Part 2. Technical. Architecture and technical stack. Implementation details and technical surprises

Base class implementation for all tests and JUnit RuleChain

The concept of developing autotests, as shown in the previous article (Part 2. Technical. Architecture and technical stack. Implementation details and technical surprises), is based on the idea of ​​a framework in which a set of system functions is provided for all autotests – they are seamlessly integrated and enable developers Autotests focus on specific issues of business implementation of test classes.

The framework includes the following functional blocks:

  • Rules – initialization and finalization of test infrastructure components as initialization of WebDriver and receiving a video test. Described in more detail below;
  • WebDriverHandlers – helper functions for working with the web driver like executing Java Script or accessing browser logs. Implemented as a set of static state-less methods;
  • WebElements is a library of typical web elements or their groups, which contains the required cross-function functionality and typical behavior. In our case, this functionality includes a possible check of the completion of asynchronous operations on the side of the web browser. Implemented as extensions of web elements from the Selenium and Selenide libraries.

Initialization of the test environment. Rules

The key class for all test classes is BaseTest, from which all test classes are inherited. The BaseTest class defines the Junit “runner” of tests and the RuleChain used, as shown below. Access from applied test classes to the functions provided by rule classes is carried out through the static methods of rule classes.

The BaseTest sample code is presented in the next box.

@RunWith (FilterTestRunner.class)
public class BaseTest {
    private TemporaryFolder downloadDirRule
= new TemporaryFolder (getSomething (). getWorkingDir ());

@Rule
public RuleChain rules = RuleChain
.outerRule (new Timeout (TimeoutEnum.GLOBAL_TEST_TIMEOUT.value (),
TimeUnit.SECONDS))
.around (new TestLogger ())
.around (new StandStateChecker ())
.around (new WaitForAngularCreator ())
.around (downloadDirRule)
.around (new DownloaderCreator (downloadDirRule))
.around (new EnvironmentSaver ())
.around (new SessionVideoHandler ())
.around (new DriverCreator (downloadDirRule))
.around (new BrowserLogCatcher (downloadDirRule))
.around (new ScreenShooter ())
.around (new AttachmentFileSaver ())
.around (new FailClassifier ())
.around (new PendingRequestsCatcher ());

// a set of methods for providers of system-wide data from file files
final protected SomeObject getSomething () {
return Something.getData ();
}
...
}

FilterTestRunner.class – BlockJUnit4ClassRunner extension, provides filtering of the composition of executable tests based on regular expressions by the value of the special Filter annotation (value = "some_string_and_tag"). The implementation is given below.

org.junit.rules.Timeout – used to limit the maximum continuation of tests. It should be installed first, since it starts the test in a new branch.

Testlogger – a class that allows the test to log events in json format for use in ELK analytics. Enriches events with test data from org.junit.runner.Description. It also additionally automatically generates events for ELK in json format to start-end the test with its duration and result

Standstatechecker – a class that checks the availability of the web interface of the target stand BEFORE initializing the web driver. Provides a quick check that the stand is available in principle.

WaitForAngularCreator – the class that initializes the web driver handler to control the completion of asynchronous operations of the angular. It is used to customize "special" tests with long synchronous calls.

org.junit.rules.TemporaryFolder – used to set a unique temporary folder for storing files for file upload and download operations via a web browser.

Downloader creator – a class that provides support for upload operations to a temporary directory of files downloaded by the browser and recorded through the Selenoid video function.

EnvironmentSaver – a class that adds general information about the test environment to the Allure report.

SessionVideoHandler – the class that unloads the video test file, if any, and applies it to the Allure report.

DriverCreator – a class that initializes WebDriver (the most important class for tests) depending on the set parameters – local, solenoid or ggr-selenoid. Additionally, the class executes the set of Java Scripts required for our testing. All the rules that apply to the web drivermust be initialized after this class.

BrowserLogCatcher – a class that reads Severe messages from the browser log, logs them to ELK (TestLogger) and applies it to the Allure report.

Screenshooter – a class that, for unsuccessful tests, takes a screenshot of the browser screen and applies it to the report as a WebDriverRunner.getWebDriver (). GetScreenshotAs (OutputType.BYTES)

AttachmentFileSaver – a class that allows you to attach to the Allure report a set of arbitrary files required by the business logic of the tests. Used to attach files uploaded or downloaded to the system.

Failclassifier – A special class that, in the event of a test fall, tries to determine whether this fall was caused by infrastructure problems. Checks for the presence on the screen (after a crash) of special modal windows of the type “System error No.XXXXXXXXXX has occurred”, as well as system messages of type 404 and the like. Allows you to split fallen tests into business crashes (as per the scenario) or system problems. Works through the extension org.junit.rules.TestWatcher. # Failed method.

PendingRequestsCatcher – Another special class that tries to further classify whether the drop was caused by incomplete, freezing or very long rest services between the angular and the web frontend. In addition to functional testing, it makes it possible to identify problematic and freezing rest services under heavy loads, as well as the overall stability of the release. To do this, the class logs in ELK all events with frozen rest requests that it receives by launching special js in the browser through an open web driver.

Test Class Implementation Template

package autotest.test.;
@Feature ("The expanded name of the subsystem as in TMS")
@Story ("The detailed name of the test according to TMS")
@Owner ("last name of the automator making the last changes to the test case")
@TmsLink ("Test number according to test link. Corresponds to the configured template")
public class <Сквозной номер тест кейса>_Test extends BaseTest {
/ **
    * Announce the login from which the target test passes
** /
Login orgTest;

    / ** We declare all logins participating in the test as auxiliary ** /
Login loginStep1;
...
Login loginStepN;

/ **
    * Here are listed the business objects that are required for the test - Test Scene
    * ... for all required business objects
** /
/ **
    * Initialization of the test scene
    * For each business object, it is necessary to display an initialized value in the report
* Utils.allure.message ("The business name of the object in the context of the test case", business_object)
    * If the class is initialized as null, then it is displayed in the report indicating at what step it will be filled.
    * Further, this type of object should be additionally displayed in the report in the preconditions or actions methods
* Utils.allure.message ("The number of the created document to be filled in in step X", documentNumber)
     ** /

    @Step ("Initializing Test Objects")
    private void init (Login login) {
some_business_object = // create the required object in or regardless of login
Utils.allure.message ("The business name of the object in the context of the test case", some_business_object)
// ... for all required business objects

/ ** Get the values ​​of auxiliary logins * /
loginStep1 = LoginFactory.get (_Some_Login_);
...
loginStepN = LoginFactory.get (_Some_Login_);

    }

/ **
    * Implementation of a specific test
** /
@Test
@Filter ("The name of the test to use filtering at the JUnit level")
@DisplayName ("The expanded name of the test according to TMS")
public void <Сквозной номер тест кейса>_<Полномочие>_<Уникальный_номер_проверки>_Test () {
// Get the value of the test organization
orgTest = LoginFactory.get (_Some_Login_);

// Initialize test data depending on login
init (orgTest);

// Follow the steps of test scripts-preconditions. Precondition steps should not depend on login value
preconditions ();

// Follow the steps of the target test script
actions (orgTest);
    }

/ **
    * Performing the required set of activity for test preconditions
    ** /
    @Step (Prerequisites)
    protected void preconditions () {
loginStep1.login ();
new SomeAction (). apply (someTestObject1, ..., someTestObjectN);
Utils.allure.message ("Received value for - Business object name in the context of the test script", someTestObjectN)
...
}

/ **
    * The method contains a declarative list of test case operations
    * /
    @Step ("Test Steps")
    protected void actions (Login testLogin) {
testLogin.reLogin ();

// Perform the required activity or set of activities of the main test
new SomeAction (). apply (someTestObject1, ..., someTestObjectN);
    }
}

Test Script Class Implementation Template

package autotest.business.actions.some_subsystem;

public class SomeAction {
    
// INITIALIZE PAGES
    PageClassA pageA = new PageClassA ();
    PageClassB pageB = new PageClassB ();

@Step ("The full name of the test script according to TMS")
@Description ("Short description of the test scenario according to TMS")
public void apply (someTestObject1, ..., someTestObjectN) {
// TMS script steps
step_1 (...);
step_2 (...);
...
step_N (...);
}

@Step ("Name of step 1 of the test script according to TMS")
private void step_1 (...) {
pageA.createSomething (someTestObject1); // just as an example create
    }

@Step ("Name of step 2 of the test script according to TMS")
private void step_2 (...) {
pageA.checkSomething (someTestObject1); // just as an example
    }

...
}

Implementing the FilterTestRunner Test Filtering Class

Here's an implementation of the BlockJUnit4ClassRunner extension for filtering tests based on arbitrary tag sets.

/ **
* Custom runner for JUnit4 tests.
* Provide capability to do additional filtering executed test methods
* accordingly information that could be provided by {@link FrameworkMethod}
* /

public class FilterTestRunner extends BlockJUnit4ClassRunner {
    private static final Logger LOGGER = Logger.getLogger (FilterTestRunner.class.getName ());
    public FilterTestRunner (Class klass) throws InitializationError {
super (klass);
    }
    / **
* This method should be used if you want to hide filtered tests completely from output
** /
    @Override
    protected list getChildren () {
// Get a filter instance that compares the given filter with the @Filter annotation value according to the required logic
TestFilter filter = TestFilterFactory.get ();
List allMethods = super.getChildren ();
List filteredMethod = new ArrayList <> ();
for (FrameworkMethod method: allMethods) {
if (filter.test (method)) {
LOGGER.config ("method [" + method.getName() +"] passed by filter [" + filter.toString() + "]");
filteredMethod.add (method);
} else {
LOGGER.config ("method [" + method.getName() +"] blocked by filter [" + filter.toString() + "]");
}
}
return Collections.unmodifiableList (filteredMethod);
    }


    / **
* This method should be used if you want to skip filtered tests but no hide them
    @Override
    protected boolean isIgnored (FrameworkMethod method) {
...
if (filter.test (method)) {
return super.isIgnored (method);
} else {
return true;
    }}
    * /
}

In the next part, I will talk about how we implemented the process of uploading a file from the container with the browser to the test framework, and solved the problem of finding the name of the file downloaded by the browser.

By the way, we will be happy to replenish our team. Current vacancies are here.

Similar Posts

Leave a Reply