About Selenium and one "bike"

Hello!

I would like to talk about the problems that I encountered during the development of Selenium WebDriver, their solution and how these solutions can, in principle, be used. All this is presented in the form of a prototype framework, a link to which will be at the end of the article.

In this post I want to share my ideas on the implementation of the Page Object template, how to handle errors that occur during the execution of tests, and talk a bit about logging. And also share information about some of the tools that are implemented using Selenium WebDriver, and their best practices.

The outline of my article is as follows:

1. Captain evidence, instead of entry.
2. A little about yourself, you need to introduce yourself ...
3. Why Selenium?
4. About Page Object ...
5. Not a bug, but a feature!
6. And again about logging and reporting.
7. But are there no analogues?
8. Promised links.
9. In conclusion.


So let's go!

1. Captain evidence, instead of entry.


image

I don’t think it makes sense to explain what software testing is, what strategies and models exist, what its role is in the process of software development and quality assurance, and what risk projects and users are exposed to if this process is absent. In addition, now programming is not an art, but a craft for many millions of specialists. Projects are implemented in a short time and with limited budgets, and often can be monsters with simply immense functionality. Moreover, products can change intensively from version to version, let’s say so improve. And each such “improvement” can be fatal.

In such conditions, automated testing plays a key role. What models, strategies and methods exist are better to look in other sources. My article is not about that. It is dedicated to automation tools for testing client-side web applications.

2. A little about yourself, you need to introduce yourself ...


I have been doing regression automated testing for four years now. We test the product’s desktop client and classes that describe its business logic. You can read about the tool that is used on our project here . During this time, a lot of UI tests, unit tests were automated, a lot of things were ... It was here that I observed the evolution of testing automation, and I took part in it.

image

At first, these were scripts written directly on the knee using automatic recording tools in which the logic of the script was inseparable from the ways of interacting with the tested objects. As the set of tests increased, it became obvious to everyone that it was necessary to develop some kind of framework and use it for automated scripts. Anyone in the application under test had very sad consequences for the entire test run.

Then it became necessary to use program interfaces for interacting with the product and databases, because what the forms of dialogs showed was not enough ...

However, something I got carried away. But it was here that I realized how important it is to think through from the very beginningstrategy and tactics of interaction with the product of developers for its effective automated verification.

Either the blind-deaf-mute or the person who was born and lives somewhere in Antarctica cannot notice the tendency for applications to move from their desktop clients to web clients. Yes, and the development for the web is now mainstream, it's no secret.

I decided not to stand aside and try in my free time to learn something that is used to automate the functional testing of the frontend - part and to check its compatibility with various client platforms. And besides, there is some likelihood that the web client on our project to be ...

I chose Selenium Webdriver.

3. Why Selenium?


image

Why Selenium?

Once upon a time, it was a small tool that only desperate enthusiasts from the world of web development used. Now it is a formidable weapon that not only many thousands of automation engineers around the world have taken into service to combat defects, but even frontend developers.
image

You can read about the Selenium product line here or here .

But I must say that this is not a means of directly automated testing. Например, Selenium Webdriver предоставляет множество программных средств и интерфейсов для эмуляции пользовательского взаимодействия с браузером и загруженным в его окне контентом. Эти средства покрывают почти 100% возможных вариантов такого взаимодействия. Все это может найти более широкое применение. Но, например, он не может, по умолчанию, формировать отчет о прохождении того или иного теста.

Далее, есть некоторые другие проблемы. Ниже часть из них:

Интерфейсы WebDriver и WebElement предоставляют очень ограниченную функциональность. It is mainly aimed at searching for elements of the active page and performing the simplest actions on elements (entering values, clicking, etc.). Many other useful features are provided by interfaces such as HasInputDevices, Locatable, Options, Alert , etc. And using only WebDriver and WebElement will greatly limit the capabilities of automated tests.

Each browser has its own driver . Now it is:
ChromeDriver;
FirefoxDrive
HtmlUnitDriver (for Unit - tests at the level of an html document);
InternetExplorerDrive ;
OperaDriver ;
RemoteWebDriver(my favorite, good for running tests on remote machines);
SafariDriver ;
IphoneDriver ;
AndroidDriver ;
PhantomJSDriver (some exotic browser, I didn’t have the patience to build it on my machine).

I think that the Yandex team will also release their driver soon.

A wide choice, for every taste and for every need. But if one or some of the above is explicitly used in the test project, this will create problems when you need to quickly switch from browser to browser or use several at once.

Therefore, I would like to have some single way or interface to create the desired instance. It is desirable that he accept some kind of setting, which can always be changed, as a parameter. This setting should include browser type, features , timeouts . Well, let the opportunity to hardcode too.

Writing classes that would be responsible for interactive work with pages. PageFactory- a wonderful tool. However, its use becomes inconvenient due to the loss of visibility of the class code describing very large pages, and repeating elements are fraught with copy-paste. The way out is to select repeating elements in separate blocks (classes) and use blocks already. But this does not solve the problem to the end. What if your service can work by opening pages on separate tabs or browser windows, all of these windows have duplicate blocks? And some of them also lie on frames! I experimented on this and this , and immediately ran into the problem described. And the situation is aggravated by the fact that the instance that implements WedDriver sees only the active page (window / tab) or frame.

I would like to be able to break the page into blocks that the working test could memorize and work with them directly, without calling driver.switchTo (). Window (somehandle) or driver.switchTo (). Frame (frameIdentity) .

Therefore, I set myself the task not just to master the above tool, but to make a project, and even better - a prototype framework based on Selenium Webdriver. And in addition - try to find analogues and make it so that something can easily integrate with them. I just think that if the task is that way, then the submitted material is absorbed much easier.

Java was chosen as the language. Development Environment - Eclipse.

I will not talk about how this path was gradually overcome. I’d better continue with a description of the problems and their solutions. Maybe America will not open, but I will be glad if someone finds something useful for himself ...

4. About Page Object ...



I think that there are few such automators who would not know about the design - Page Object pattern. By the way, this pattern is also applicable for writing UI autotests of some desktop application. But now we will talk about its implementation in Java for the web interface.

You can read about the reception here .

Now I will show you the currently working code of some not very successful test.

Code

I think everyone understands what is good when you want to write a script where you need to make one input somewhere and 2-3 clicks on something. You can quickly fix such a test if something has correctly (!!!) changed.

If the test should check more than the steps described above, then the code sheet that is inconvenient for reading will turn out. Now, let's multiply all this with a huge test project. Have you already imagined how the poor tester cries, what supports him, after every change of Google? And so that he doesn’t cry like that, we need classes through which the test performs its actions on the page. And even if something has changed (once again, this change is a feature, not a bug introduced), but the edits will be local, and not in the whole project.

There are many implementations of Page Object, including, I think, on your projects. Below I will demonstrate my implementation of the same test:

public interface IPerformsClickOnALink
public interface IPerformsSearch
public class SearchBar
public class LinksAreFound
public class AnyPage
public class Google
test

And if you work hard and do something like this setting , then the test may work like this .

Many experienced automation engineers probably thought now: “Well, yes, Page Factory uses ... For some reason, the author has complicated the description of the google main page - it can be described as one class! Well, perhaps some obscure annotation appeared ... @PageMethod is called. The descendants of the Page and Entity classes ... We saw a similar thing here . Author, we have all seen this! ”

Well yes. This was a simple example. I intentionally complicated the description of the main page to show that it is possible to break the description of the same page into two independent classes. I’ll talk about this annotation below ...

And now the example is more complicated ...
The picture shows a certain situation.
image

What the test does:
1 goes to docs.google.com
2 opens a Google-accessible document
3 on the service’s main page calls the sharing settings dialog;
4 from the open document, the same dialog is invoked.

For clarity, the entire sequence of actions shown in the form of screenshots. Active elements are highlighted:
image
image
image
image
image

It is necessary: somehow to process both instances of this dialog in different windows. And this is what happened to me. This is the precondition described.

Now I will click on the field in which they are asked to enter names, email addresses, etc., in the pop-up element that appears, click “Cancel”, after which the “Finish” button will be clicked. Actions will be performed in both windows. For clarity, below is the sequence in the form of pictures.

image
image
image

So we effortlessly coped with the task. . This is the code of the getCustomizingAccessDialog () method for the document and the main page.

Those. there is a pop-up block of elements located inside the frame, which we used as an object that exists independently and is picked up when necessary ... We know that it is on the page of any document and on the main page of the service, and we get it from them.

In fact, for a long time I tried to find the best way to organize and describe this or that test object in different sources. Although, maybe I was looking badly ...

Obviously, you need to group page elements into blocks for reuse. These blocks can even be transferred outside under the guise of independent objects and work directly with them. If this is not done, then the class will increase the number of attributes and methods. Need decomposition. Otherwise, you will get these objects:
image

If you look at the example described above, the methods for working with the main content are added with methods for working with each type of pop-up dialog - access settings, modal forms of renaming, choice of options, comments in documents.

The above solution can be implemented painlessly when testing what is loaded in a single browser window (single tab).

But in the case of the described rather complicated behavior, you will have to somehow switch the webdriver from page to page, not forgetting to switch to the desired frame.

And then an idea came up! But what if you teach each such object to remember the path to itself and automatically switch webdriver when calling the method of interactive interaction with the page.

This, it seemed to me, is possible if we move somewhat away from the concept of “page”. Those. by it I will mean not the downloaded content, but a browser window with the content loaded in it. This window has a unique string identifier, by which you can easily identify it among others. OK! That is, before calling the method, a switch to the desired window will occur.

So. What to do with frames? You can also make sure that each block of elements knows additionally which frame to switch to.

So I am building a path.

As you can see from the list of class constructors that I called Page(the name is very conditional, perhaps the class should be renamed), you can write a description of the loaded page in its entirety, or you can do it in parts, in addition to indicating another object similar to it as a parent when necessary. Thus, you can build a linked graph of element blocks. It may be something like a hierarchy.
image

OK! Well, the paths indicated, the hierarchy built. What's next? How to implement automatic switching during a call, for example, clicking or entering a line in a field? Switching itself is implemented in this way.

But! Many may ask the question: “Do we need to re-call it in each method?” No. Below I will try to explain how it works.

In my decision, I propose using not the objects created by the constructor themselves, but their substitutes (proxy, in other words). The cglib library was used for this . And to be more precise, the MethodInterceptor for intercepting the called methods, Enhancer for creating the proxy objects themselves and Callback . Using the types listed above eliminates the need, in the future, to create a set of interfaces for each Page descendant (see java.lang.reflect.Proxy ) and makes the implementation more flexible. However - there is a limitation - the inheritor of the Page class should not be final !!!

Those. when we try to call one or another method of a Page - object (the inheritor of the Page class), this method is intercepted and the object "switches" to itself. And after that the execution of the method itself.

Perfectly! But does this kind of interception happen when each method is called? The question is fair. Do not forget that the Page - object is also a java language object. Such an object may have public or protected methods that are in no way associated with browser interactivity. How to be Obviously, you need to come up with a way to divide methods into methods responsible for interactive interaction and all the others.
image

Abstract @PageMethodjust serves as such a “separator”. That is, if the method is marked with such a marker, then a preliminary switching to the page or its fragment will be performed ...

In general, the solution looks like this.

Next, a little about the possibilities of creating Page - objects.

Any block of elements or page presentation can be created in three main ways:

- from the descendant of the Entity class - this is something like a model of a service open on the main or any other page as a whole. In the described examples, this is Google (search engine), GoogleDocsApplication (Google Drive). By the way, the example from Google demonstrates that with the “application”, in principle, we can also work as with a Page - object. I kind of included elements with a panel and search results in it.

“Essence” is able to spawn those objects that are in the first (main) open window / tab of the service (example with Google Drive, “ docs.getContent (); ” - wrapping around one of these methods, example with Google - “ searchBar = get (SearchBar.class); ”- call of one of these methods) or those that exist in newly appearing windows / tabs ( AnyGoogleDocument anyDocument = docs.get (AnyGoogleDocument.class, 1) ;).

The picture shows the entire list of methods of the Entity class that are already ready for use or can be overlapped in the heirs:
image

- from another descendant of the Page class.That is ... We have a list of available Google Drive documents, on which we perform various actions - open, put tags, work with the pop-up menu and buttons on the action bar. We describe all this in a separate class (in the examples above - GoogleDocsMainPageContent).

The elements that appear as a result of these actions (access settings dialog, various modal forms, etc.) are parts of this page. But they can functionally be considered as if in isolation from it.

Such elements can be described in separate inherited classes of Page.

In the same way, for convenience, larger, but more static blocks (for example, a table or document editor) can be described.

In all described situations, objects can be created with the parent indicated.

The picture shows the entire list of methods of the Page class that are already ready for use or may be overlapped in the heirs:
image

All the methods for creating objects described above for testing are wrappers around the class methods in the screenshot:
image

- using TestObjectFactory . The method is so far good when there are components that can work on their own, without using the service. For example, a document, spreadsheet, presentation, etc. can be opened without the help of the service, provided that there is an access right and a link to the document. If you need to check just such a situation, then you can do so . Next, the second use case is repeated. But this option I have so far the most unworked.

I could talk about the underlying mechanisms and jokes of this system. But the chapter will be very large. This may be the subject of a separate note.

5. Not a bug, but a feature!


image

Any exception that occurs during the execution of an automated test signals a problem.

This may be a signal of a developer bug. The test itself can be poorly written. And this screamer should be fixed already. The tools themselves are no exception. The list of Selenium defects can be found here .

But this is not what I am talking about.

How do you like this situation? For example, a defect was detected at the manual testing stage. It is listed in the bugtracker, it may be “Minor”. This bug is not fatal to the user, he can easily get around it. At the moment, developers have more important tasks, and the fix of this defect can be left until better times. And now we need a regression test, the path of which runs through the described defect. The test was written, and ... Oh my god! It crashes with some terrible exception that caused our bug.

The situation, I tell you, is not pleasant. The best way out is to make some kind of crutch that would allow you to catch the error and pass the test to the end. Otherwise, a terrible thing can happen - the test will only reach this uncritical error, it will fall and will not check the rest. And it was there that some kind of grief climbed - the developer and created chaos there. But writing “crutches” can complicate the test script itself or classes that describe what is being tested. The defect has already been fixed, but they can forget the “crutch”. Over time, the entire project may become similar to this.
image

Situations are even worse ...

A striking example is the occurrence of a StaleElementReferenceException . You can read more here. This is an example of when during normal operation of the tested application situations arise for the processing of which the tool used was simply not designed!

For example, I came across this when I was trying to write “tests” for a list of Google Drive documents and a spreadsheet document. The picture shows after what action most often such an exception occurred (click on the highlighted green element).
image
After changing the value of the “asterisk”, the further passage of the “test” was like a trip through a minefield. The error occurred when trying to return the "star" to its original state, or to work with the checkbox located to the left, and sometimes when trying to open the document.

Different ways to deal with this error are offered -from re-performing the same actions (searching for an item and performing actions) to increasing the virtual memory of the machine. OK! Suppose the second I can not do. Then the first remains for me.The methods for working with the list would then look like this (+ possible variations):

try
{
// some actions
}
catch (StaleElementReferenceException e)
{
// same actions again
}


Yes, it works. But is it easy to maintain a class with each method described in this way? And if it is observed in any tested feature?

The decision was ripe before I tried to “test” Google Drive.

I came up with the ITestObjectExceptionHandler interface . And this is an abstract class that implements it. Constructors and throwableList attributeadded to simplify the process of possible exception handling. The purpose of the class object that will inherit it is to try to handle the exception and return some value, or throw a new exception.

Further.

Back to the previously described Page and Entity classes . They have a class - ancestor with these attributes. The interceptor ancestor for Page and Entity has such an implementation. And once again I remind you that this is how the interceptor works for the Page class and its descendants, the necessary one is highlighted / ** / . The implementation for the Entity class is very similar.

That is, we can execute the method. But if there is a chance of an exception that should be handled in a special way, the whole logic of such processing can be moved to a separate class. And in order for this algorithm to work, you need to do it .

For Google Drive, I created a class that describes what to do when a StaleElementReferenceException occurs. And the constructor of the abstract class that describes the work with the Google Drive list, I decided to do so .

Result: StaleElementReferenceException generally ceased to manifest itself when working with a table. With a list of documents on the main page of the service, too (Hooray!). Apparently, for Google this was enough.

You can do the same for situations where there is some kind of old bug that interferes with passing the tests.

For instance. There is a situation:
1. We take turns creating documents on Google Drive
2. Close the pop-up modal forms
3. Rename
4. On some we put an “asterisk”
5. Close

To make it clear to everyone, the whole sequence is shown as screenshots in which the active elements are highlighted :
Creation
image
image

Working with a modal form
image

Renaming
image
image

Label in the form of an asterisk. Spreadsheet document
image

The fact is that when performing the described actions without any expectations, when the document is synchronized with the server, an alert occurs. If it is not processed, the test will fail. Reason: UnhandledAlertException. I know with you that this is the wrong test. But I want to pass off the situation as an uncritical, but nasty from the point of view of automation, application bug. It will be fixed later, but the test, going to the end, should already be now.

According to the plan, after these actions:

1. Check the renaming of each created document (each of a different type) on the main page of the service;
2. Checking the ability to delete all these documents.

If these actions are not performed, then there is a risk of missing very serious bugs, and users will be horrified.

I solved the problem like this:
1.The handler itself.
2. Its use (in the test) .

Result: the test passes to the end. His status: FAILED TESTS BUT WITHIN SUCCESS PERCENTAGE. And this is how it signals a problem:
image

Attention (!!!). Such handlers should be implemented as simple as possible. It is desirable that each class of such handlers has a unique set of exceptions that should be addressed.

6. And again about logging and reporting.



imageimageimage

Turn on the cap briefly. Probably, everyone understands that in addition to directly passing the tests, there should also be some kind of exhaust, thanks to which you can judge the current state of the product. Well, or some detail of passing the tests is needed ... These tasks just solve logging and reporting.

Since I used java for experiments, my head hurt a lot when choosing the right framework for logging. And there was plenty to choose from! The author of this article very well described the situation with logging in java . And at first I myself did not know what I wanted to receive. In addition, it is necessary that a report can be built using this log.

As a result:
- I need a simple logger that could log messages, their level, output data to the console - at least;
- it is very good that objects of exceptions can be “clung” to it if they arise;
- and very good - the ability to attach a link to some file that turned out during the test to the log entry;
- Write video with special effects.

I think everything is clear with the first two points. Third. A file can be anything, an xml file, typed text, or even a screenshot taken from a browser window. This can be convenient when, for example, processing log entries.

As for the reports:
- I need a report that would be formed both during the run of one test and after the launch of the entire test;
- In this report, I would like to see a description of the steps taken on the selected test;
- It is desirable that such detail could display screenshots or some other visual information.

I will not forget to mention that I chose TestNG as the framework for conducting the tests themselves . Why? First, because it has the ability to generate test reports. But after a more detailed study, I realized that it has many other advantages, such as integrability with maven and ant , the ability to be used by continuous integration systems like jenkins and hudson , multi-threaded work ... Very well, this wonderful framework is described in this article . Anyone interested can see the documentation.

Но, что-то я отвлекаюсь…

В качестве логгера я решил использовать java.util.logging.Logger. Почему?

— он есть в любой java'е начиная с 1.4, лишние зависимости на проекте ни к чему;
— его легко интегрировать с логами браузера (Mozilla Firefox, для других баузеров извлечение логов пока не реализовано);
— в случае использования других библиотек логирования (log4j, slf4j или logback, например), всегда есть возможность направлять сообщения в их логгеры.

Но:

— хотелось бы избавиться от явной инициализации логгера;
- There are complicated use cases - building a message hierarchy. A large number of levels, which, in principle, should correspond to the debugging information - FINE, FINER, FINEST ... It is necessary to somehow cut off everything unnecessary.
- the selected logger, as well as the ones listed above, are not able to attach files to their messages. This is not about FileHandlers or something similar.

Now the decision.

In my project, I created a class that I named short and clear - Log. Its main methods:
- debug (String) - generates messages with the FINE level , I suggest using for debugging purposes
- error (String) - generates messages with the SEVERE level , signals about serious errors;
- message (String) - generates messages with a levelINFO , signals about the normal state;
- warning (String) - generates messages with the Warning level , signals about possible problems;
- log (Level, String) - for those who still want to generate a message with any of these levels .

The same methods with an additional parameter as an object of the caught exception:

- debug (String, Throwable);
- error (String, Throwable);
- log (Level, String, Throwable);
- message (String, Throwable);
- warning (String, Throwable).

Further (attention!), Methods with the ability to link a file and write a log:

- debug (String, File);
- error (String, File);
- log (Level, String, File);
- message (String, File);
- warning (String, File).

In fact, all this is a simple wrapper around java.utils.logging.Log and java.utils.logging.LogRecord . These are all static public methods that can be accessed anywhere in the project.

Usage example . Can be used instead of comments. However, you will surprise someone with this.

So, how do I “hook” a file to the log? The fact is that this logger does not create LogRecord objects, but its descendant, which I called LogRecWithAttach . Everything is simple! Anyone who finds 10 differences from the “ancestor,” 5 points and an increase in karma.

OK! But someone might already have a question: “Author, why do you need such excess?” I’ll try to answer ...

Well, firstly, one of the important functions that I wanted to implement was to take screenshots from the page. I believe that screenshots could be a good addition to the log and reports. And for convenience, it seemed to me, it would be better if the log entry contained a link to the photo page.

The functions for taking screenshots are performed by a class called Photographer. A list of his methods can be found here:
image

His functions are as follows:
1. Take a screenshot from the active page, save it to a file;
2. Creating a log entry with the desired level of importance, adding a link to the saved screenshot to it;
3. You probably noticed that WebElement is specified as the parameter for some methods. These methods highlight the element on the page, take its photo and return the element to its original state. Examples of such screenshots were shown earlier. Moreover, the color of the backlight depends on the "correctness" of the state of the selected fragment of the page. So, an incorrect (well, type) state is photographed in the screenshot: The
image

list of documents with incorrect contents (under the “test” condition, the text document and the MS Access database should not load) is highlighted in bright orange. Such a photograph was taken by one of the calls to the Photographer.takeAPictureOfAWarning method (WebDriver, WebElement, String).

True, there is one BUT (!!!) - elements are selected using javaScript. There was an idea to make projections in screenshots on the coordinates of the elements. It even worked out. However, for elements that sit inside frames or are located on pages with scrolls, such projections (circled areas) were obtained with an offset. I had to abandon the idea. But on the other hand - now there is the opportunity to record video with special effects! ..

In general, my photographer seems to supplement the log.

The second, not bad, opportunity is to attach any file, in principle. For example, is it really bad to find a link to a file in a report that displays drilldown built on the basis of accumulated messages? For example, the one that was downloaded to Google Drive from the example above. And follow this link to open it for viewing.

So, I am smoothly moving on to building operational reports on the passage of one or more tests.

To begin with, for those cases when you need to convert the log taking into account the features described above, I came up with an interface that I called IlogConverter .

And here is an example of use. For example, we additionally use the log4j library. We need to have file data in this logger. You can create a class whose object would check whether the incoming message has an attachment. And if it is, an additional log4j message is generated. And so that this object can "listen" to the log, I propose such a method - Log. addConverter (ILogConverter). This mechanism works thanks to this code .

And this is what happens when a new message appears:
converting.convert (rec);

I made for my framework something like a “standard” implementation of the named interface, which is responsible for generating the details of the TestNG report. I called her ConverterToTestNGReport. What she does? It intercepts messages from the log, according to a specific html-template (which I have hard-coded as a final attribute so far) forms a report line:
Reporter.setEscapeHtml (false);
Reporter.log (htmlInjection);


Additionally - if the message has an attached screenshot, then we turn it into "
Result - the following report fragments. I want to draw attention to the fact that these are not customized reports.
image
image
image
image
image
image

Of course, it may be rude, but for the first time it will do. I think it's worth figuring out if you can customize the details of the TestNG report. If so, then you can make such a custom and use it instead of a hard-coded template.

And so far so good. But that is not enough. Let me explain ...

Ok! We have an easy to use logger. We even have a tool that allows you to build a report. But you still need to make sure that the status of the passed test is synchronized with the maximum level of log messages.

I explain. If nothing is done, the test will have the status SUCCESS if it passed to the end and did not fall. If an uncaught exception occurs during its execution, the test will have the status FAILURE. If the test is dependent and the defining test did not pass - SKIP . Now let's ask ourselves the question: “Is it possible to consider a test to be fully passed if something happened during its execution, because of which entries with the WARNING or even SEVERE level got to the log?”. That is, the passed test has the status of SUCCESS, however, looking at its detail, we see yellow and red lines. I consider this at least misinformation.

My solution is below.
I implemented a class that associates a thread (and TestNG supports multithreading) with the test itself . Further, log messages are stored with reference to a specific test (an instance of a class that implements ITestResult) At this point I will not stop. I will demonstrate how the log message is linked to tests. It is produced by the object of the aforementioned ConverterToTestNGReport . And finally, I implemented the ITestListener interface in the form of a class that:
- before starting tests (test methods), adds an instance of ConverterToTestNGReport to the log as a listener.
- after completing each test method, it synchronizes the status of its passage with the fact that the log was recorded:
1. if the test passed to the end, but there are messages in the log with the SEVERE level , the status of its passage changes to FAILURE.
2. if the test passed to the end and there were no messages with the level of SEVERE or WARNING, its status remains unchanged - SUCCESS .
3. If messages with the WARNING level were recorded, I suggest changing its status to SUCCESS_PERCENTAGE_FAILURE . Or let the "user" choose the appropriate status for himself .

And now the finale!
To build the reports described above, you need to perform the following action:
@Listeners ({ReportBuildingTestListener.class})
public class NewTest {


In conclusion to this chapter, I would like to say that logging is already largely automated. Those. any interactive action is graphically recorded - value entries (before and after input), clicks and submits (before the action is completed), the appearance of new tabs / browser windows, the results of clicking on links. For everything else, some “standard” text messages are generated.

Some of these functions are taken over by classes, which are a layer between the tools that make up Selenium and classes of a higher level.

This is ExtendedEventFiringWebDriver - the descendant of EventFiringWebDriver , which actually does all the dirty work. Its "drying up" is carried out by the implementation of the IExtendedWebDriverEventListener interface(extended WebDriverEventListener ), which is part of WebDriverEncapsulation as an nested class. WindowSwitcher and SingleWindow , the browser window / tab manager and presentation of the window / tab itself, take over some of the logging functions .

Logging itself can be configured for the project as a whole. However, I would like to talk about configuration and these classes in another article, which might be ...

7. But are there no analogues?



imageimageimageimage

So! One of the tasks was to try to find analogues and try to work with them. This broadens the mind. In addition, many useful features are already implemented. And then why write a bike that does the same thing? Better anyway, it won’t work out! It’s much more useful to see how the necessary functions are implemented and learn how to use them.

But in fact, what is the analogy?

Functionality, which I absolutely refused to implement, is working with objects whose classes implement WebElement in one form or another. Those. I decided not to do anything in this direction, because stumbled upon a video - report on itwonderful framework. And rather, interest arose in trying it out in practice. I fully succeeded during the experiments with Google Drive. And it was possible to establish interaction with yandex-qatools htmlelements without any effort. For example, this is how the constructors of the abstract class look from which all Page objects on this demo project are inherited. But I got such a set of attributes for a class that describes the general properties of all Google documents. Do you see WebElement here? Everywhere typed attributes that describe the page fields, and some of them are those that are included in the packages of the named library. Some of the elements that were needed were not there. But with the help of this tool they were easily described .

Moreover, all this was possible without sacrificing the functionality that I was laying on. Quite the contrary. The decomposition of page descriptions, which I talked about earlier, has become even more visual. This is a win-win situation!

Another interesting technique for working with fields on a page is presented using the example of the inheritor of the PageObject class, which is implemented in the thucydides framework (more on that later).

So, the arguments in favor of not doing useless work in this direction are more than enough.

In the process of my “research” I came across another tool - the already mentioned thucydides. Reading articles about him, looking at the repository and trying to write something, I came to the conclusion that it is best to use his capabilities and make friends with him somehow. And believe me, there are many reasons for this:
- step-by-step organization of automated tests;
- the ability to describe scripts using the JBehave framework , and not just as working code;
- A set of convenient methods and tools for working with pages;
- Integration with JIRA and other issue trackers.
- reports. About the cover. History. Etc.
1. image
2. image
3.image

To tell you the truth, I had an idea to make a second demo project in conjunction with thucydides. There was not enough time, or enthusiasm. But perhaps I will come back to him. I just want to share the idea and find out how feasible it is. And now I’ll tell you about what I managed to implement:

- “Cross-driver” has been implemented .
- Ability to work with multiple open browsers in one test.
- implemented configurability.
You can make one setting for the entire project with an indication of the browser used, Capabilities parameters, timeouts (wait timeouts for Webdriver, your own - related to the wait time for new windows / tabs, switching between them).

You can make a set of additional / individual settings that may partially overlap the general.

Those. - in general, the browser, its capabilities / settings and a set of timeouts are indicated. In additional - only the browser and features. If you create a new Webdriver instance using this setting, the specified browser will open, configured as necessary, but its timeouts will be taken from the general setting.

This can be used, for example, to parameterize tests. Parameterization example for TestNG . My example .

- mechanisms for managing browser windows / tabs (including loading the necessary pages) without reference to the content.
- a set of tools that hide a webdriver instance at the highest levels. All work happens through them. What is the benefit - all the dirty work associated with the use of interfaces such as HasInputDevices , Options , JavascriptExecutor , etc. already done. Examples.

If it makes sense, I will talk about the above in another article.

- the method of organizing objects that implement interactive interaction described in 4 and 5.
- the logging described in 6 and the method for constructing reports using the TestNG report example.

As you can see from the above, I tried to focus on working with WebDriver and related components.

What I don’t have in thucydides (impression with an even distant signostv) in this regard:
- The ability to track and build a map of opened pages. Classes Pages and PageUrls . I can only go directly to the link, go back and forth;
- tools for working with elements, an additional set of expectations . PageObject .

And now the idea is in general:
1. I want to use my mechanism to open the browser.
2. The object of the descendant of the Entity class described earlier - wrapper for the Pages object. Thus, I will try to combine the functionality of both classes.
3. You can somehow combine the functionality of the inheritors of the Page (chapter 4) and PageObject (thucydides) classes.
4. Let's go back to the report with the number 3. As you can see, the steps can be very large (the very first one took a minute, there are 30 or more seconds). Surely these are not atomic steps. The screenshots presented in this report are the states at the end of the step. What if the step failed? For example, I would like to see illustrations of what brought the product into an incorrect state.
So, I think, if you turn the focus described in Chapter 6, provided that there is the opportunity to create a step detail in the report that can be called from the outside during its execution - that would be great!
5. Everything else does thucydides.

Like that!

At the moment, I think the ideal option is not to develop some kind of monster-like system, the functionality of which cannot be expanded through the use of third-party tools. I would like to get an easy-to-use tool in the output, which:
- could be a system-forming component;
- Either could work together with other tools by replacing existing functions with more suitable ones or supplementing their functions with something that is not found in other testing automation tools that use Selenium Webdriver as a basis.
image
I would like to talk about some thoughts on this subject in conclusion.

8. Promised links.



The result of my experiment.
An example project for testing Google Drive. Implemented using Yandex QA Tools Html Elements. Perhaps something can be done better. But I studied myself. This project uses FireFox, Chrome, Safari, and RemoteWebDriver to invoke Chrome.
Just in case, put a * .jar file. If someone is interested in looking at this project in work, then:
1. Use jdk7.
2. You need to create a maven project.
3. Add a library. Do not forget about selenium (version 2.35) and TestNG!
4. Add dependencies to the pom.xml file .

But I warn you in advance that there may be problems. No documentation. It is possible that this will serve as a guideline.. I apologize for the clumsiness, if any. Everything was done on the knee. At the time of publication, something had become irrelevant. I will try to fix it. There is simply no time for normal documentation. And is it needed for a project that you yourself treat as an experiment, even if it’s interesting?

9. In conclusion.



image
So my article is almost over. Now I’m re-reading it for the last time ...

Looking back, I see that this is a rather big review that I didn’t even count on. I would be very happy if this article helped someone in something, or someone made a small discovery for themselves.

As for me, in any case, I have achieved my goal. I tried to independently dig in the direction of previously unknown technologies for automated testing, to share some of my ideas and best practices. This article does not cover all the problems that have been encountered, and accordingly, not all solutions have been considered. If this post seemed interesting to someone, I can write a sequel, but later.

The result was a more or less working prototype tool. So far, this is just an experiment, the potential usefulness of which I myself have no idea. I would like to continue it and develop it to something serious. In this case:
- I would try to improve the existing cross-browser compatibility. Now there is an opportunity to work with:
FireFox (used 21.0);
Chrome (26-30);
Internet Explorer (used 9.0.8112);
Safari (5.1.7 for Windows);
Opera (12.16)
HTMLUnitDriver - added just in case;
RemoteWebDriver with launching any of the listed browsers.
- I would try to add support for mobile browsers iPhone and Android, as well as PhantomJS.
- I would try to improve reporting for TestNG. I would try different options for their customization .
- One could move towards integration with Junit .
- explore and improve integration capabilities.

It's great if someone liked the idea. I will be glad to know the critical opinions (but constructive), suggestions, tips and suggestions.

See you!

Also popular now: