📜 ⬆️ ⬇️

We test in the browser with the help of Geb

Geb in practice


I, for example, love when the robots do all the work for me. Therefore, I consider it necessary all kinds of scripts, inspections, spell checkers and, of course, automatic tests. By the way, how do you like this testik:

Browser.drive(driver: new InternetExplorerDriver()) { go "http://www.google.com" $('form', action:endsWith('/search')).q = '   geb  spock' $('button', value:'').click() waitFor { $('#search') } assert $('#search').size() == 1 assert $('#search').find('li.g a.l').size() > 0 println " : " + $('#res').find('li.g a.l', 0).text() }.quit() 

It seems to me that such tests have a high degree of readability - it does not even matter what language it is. It is possible to write a few more similar tests on the same model, without having any idea about Geb, Groovy and how it works. But for a full understanding of a little dig into the basics.

About Geb and Selenium


Who today has not heard about Selenium ? Probably the one who has not smelled gunpowder and still thinks that all browsers are the same. For a long time I will not talk about it, you just need to know:
Why is all this so relevant? In modern web applications, an incredible amount of JavaScript-code, special effects, animation, AJAX and other delights. There are no normal tools to control quality without a browser in general.
')
In fact, Selenium is a full-fledged robot for testing in conditions as close to reality as possible. (Historically, you can still recall Rational Robot, which was sharpened by Internet Explorer and had rather poor interaction with the page.)

Selenium itself provides several options for writing tests:
  1. Using javascript. Tests are run directly in the browser and have direct access to the downloaded web page, which allows them to perform any manipulations.
  2. Through the managing server. In a separate process, test code is run in an arbitrary language. This code calls a special program - WebDriver - which transmits commands to the browser and allows you to control the state of the page.
Not everyone likes to write tests on JavaScript, so the second method - WebDriver - is becoming more common. It is suitable for developing tests in any language, it is also convenient in that it allows you to perform tests on another machine.

So, Geb is another test-writing tool using WebDriver and based on the Groovy language. You can use Geb inside the Groovy script using the following magic words:

 @Grab(group='org.codehaus.geb', module='geb-core', version='0.6.2') @Grab(group='org.seleniumhq.selenium', module='selenium-api', version='2.14.0') @Grab(group='org.seleniumhq.selenium', module='selenium-firefox-driver', version='2.19.0') @Grab(group='org.seleniumhq.selenium', module='selenium-ie-driver', version='2.19.0') import geb.Browser import org.openqa.selenium.firefox.FirefoxDriver ... 


Geb language


One of the features of Groovy (like all dynamic languages) is that it can be easily converted into another completely unrecognizable language - for any needs.

In our case, Geb provides a browser control language. You can do everything that a human user can:
Example:

 Browser.drive { go "http://www.gramant.ru" $('.block .caption', text: 'Grails') .closest('.block') .find('a', text: startsWith('')) .click() assert $('title', text:'Grails | Gramant').size() == 1 } 

As you can see, Geb allows you to use syntax that resembles jQuery (but does not coincide with it completely). This is convenient for finding the necessary elements in the page. The click () method allows you to click on DOM elements, the << operation - to send text to the browser, and so on.

Enough to navigate. True, people still know how to read information on the page. To do this, it would be good for him to first understand what page he is on now. Let's think for a start, what is a testing page in general?

Pages and Modules


We commanded
 go 'http://www.gramant.com' 
and went to gramant.com. Now you can “read” this page, i.e. analyze and find errors. So? Not really. When the browser hits the page, it may happen:We think that we are on one page, but we are on another. Further tests will go the wrong way . Therefore, it is very important to understand which logical page we are on.

Geb suggests using logical pages (Page) and so-called modules. We will try to illustrate this with the following example:


Why do we need such an abstraction as a page ? It would seem that just look at the current URL and we will find out. For example:

 class SearchResultsPage extends Page { static url = "/yandsearch" } 

But it happens that the same logical page can have many different URLs and states, and it is not always correct to determine the page by URL (which can change unpredictably). You can do this, for example, by the title:

 class SearchResultsPage extends Page { static at = { $('title').text() ==~ '.*:  .* ' } } 

There are other options for determining the current page; as you can see, it all depends on the application. Sometimes many different URLs correspond to the same page; Sometimes, at the same URL, depending on the internal context, several different pages may appear. In general, the pseudo-property at contains the logic to determine whether the browser is currently on this page.

A few words about the modules. The diagram shows that the module is part of the page (block), and there are modules that are present on several pages. For example, the Yandex search bar is available both on the portal page and above the search results. The meaning of the modules is the reuse of the testing code.

Modules can be defined, for example, as follows:

 class LoginModule extends Module { static content = { username {} password {} loginButton { $("input", type: "submit") } } } 

And then use them like this:

 class HomePage extends Page { static content = { login { module LoginModule } } } Browser.drive { to HomePage login.username << 'user' login.password << 'password' login.loginButton.click() } 

There is, of course, the ability to define many instances of a module within a page - this is relevant for all sorts of lists. For example, search results - each result can be declared as a module. This is done using the moduleList construct, which I will not dwell on.

AJAX and everything, everything, everything


In modern times a lot of amazing things can happen inside a browser:
Testing such things automatically is possible only with the help of in-browser tests. Geb offers several tools:

Access to javascript variables

Through the js object, you can access the value of JavaScript global variables:

 Browser.drive { assert js.myGlobalVar == 1 } 

You can also use global functions:

 Browser.drive { js.globalCall() assert js.globalFunc() == 1 js."document.write"("go geb!") js.exec("return document.location.href") == 'http://www.gramant.ru' } 

I used this mechanism a little, so I assume that Geb will try to correctly convert all types of JavaScript data (as described here ) to Groovy types, but I don’t give a tooth.

It is convenient to use jQuery to emulate mouse events. Geb provides a special jQuery property for such calls:

 Browser.drive { $("div#a").jquery.mouseover() } 

This line will work only on the condition that jQuery version 1.4 and higher is loaded on the test page. In fact, such code is translated into js.exec () .

Waiting condition

You can wait for something: either setting some JavaScript variables, or the appearance of certain information on a page using the waitFor construction.

 Browser.driver { go 'http://www.youtube.com/watch?v=8d1hp8n1stA' $('button#watch-share').click() waitFor { $('#watch-actions-share').displayed } $('#watch-actions-share').find('button.share-panel-embed').click() waitFor { $('textarea.share-embed-code').displayed } println "Embed code   : " + $('textarea.share-embed-code').value() } 

This script gets the embed code for the YouTube video by clicking a few buttons and waiting for the animation to complete.

Of course, the waitFor method will not wait forever and will crash upon reaching a certain timeout, interrupting the test. The default is 5 seconds.

Using WebElement and Actions

We mentioned about Drag & Drop. At the moment (version 0.6.2), Geb does not have a convenient abstraction for performing such operations. However, it is always possible to use Selenium directly by accessing an instance of WebDriver (via browser.driver):

 WebElement underlyingElement = $('#myElement').getElement(0) Action action = new Actions(browser.driver) .clickAndHold(underlyingElement) .moveByOffset(15,15) .release() .build() action.perform() 

The Actions class is a low-level way to create a sequence of browser actions in order to perform them sequentially using the perform () method. I draw your attention to the fact that Actions are more powerful in their functionality than just calls to JavaScript code and far from all user actions can be simulated through JavaScript. The implementation of Actions on the browser side depends heavily on which driver is used and is not portable. Using Actions is practically the only option for generating tap-events on touch-screen tablets. There are restrictions - Actions do not allow you to make manipulations with Flash-components on the page; this is only possible via ExternalInterface using JavaScript, which is much more complicated.

Geb inside grails


To use Geb in Grails, it is convenient to use the Spock plugin . I will not go long about the Spock Framework, there is enough information about it. Spock is more convenient than the standard JUnit, primarily because it allows you to write tests in your meta-language specifications (again, based on Groovy). This is both shorter and more expressive.

A few words about setting up Geb for Grails 1.3.x. An example of such a project is posted here . The required section in BuildConfig.groovy will look like this:

 dependencies { test("org.seleniumhq.selenium:selenium-htmlunitdriver:$seleniumVersion") { exclude "xml-apis" } test("org.seleniumhq.selenium:selenium-chrome-driver:$seleniumVersion") test("org.seleniumhq.selenium:selenium-firefox-driver:$seleniumVersion") test "org.codehaus.geb:geb-spock:$gebVersion" } plugins { test ":tomcat:$grailsVersion" test ":hibernate:$grailsVersion" test ":geb:$gebVersion" test ":spock:0.5-groovy-1.7" } 

The current gebVersion is 0.6.2, substitute the desired one.

We know that there are two types of tests in Grails - test / unit and test / integration. Spock also adds test / functional - “functional” tests.

Geb inside functional tests (Spock specifications) is similar to ordinary, but you do not need to launch the browser and configure the driver inside the test - everything has already been done. An example of a simple specification (we assume that the HomePage, LoginPage classes are already described by us):

 @Stepwise class CoreSpec extends GebReportingSpec { def "unauthorized user goes to login page"() { when: to HomePage then: at LoginPage } } 

Each method inside the specification is a separate test. At first, this test tries to enter the main page, but since the user is not authorized, he should end up with LoginPage.

Of course, there are Geb configuration files that define:
In the demo, there is an example of a GebConfig.groovy file.

It seems to be enough. We collect:

 grails -Dgeb.env=firefox test-app :spock 

and see how our robot is trying to test something there.

It quickly turns out that our tests do not work on Chrome and IE. The reason is that it is really possible to launch either HtmlUnit (which is useless for testing) or Firefox from scratch and without any available tools. For Firefox, a new profile is automatically created that “expands” the browser for later management via FirefoxDriver. Yes, Selenium / Geb works great on Firefox, because it was originally designed for Firefox.

As for IE and Chrome, everything is a bit more complicated with them. The general situation can be understood from the table:
BrowserWhat is requiredRestrictions
firefox Firefox-Under some OS is subject to the problem of exhaustion of TCP ports .
ChromeYou will need to download and install a separate platform-dependent service (ChromeDriverService).There is no support for Actions .
Opera-Not lower than version 11.5, only one instance of the browser at a time.
Internet ExplorerRequires the launch of a separate service. Requires browser pre-settings (Protection Mode).Versions 6,7,8,9. Supports exactly one instance of the browser.
As you can see, the Chrome driver does not support Actions! Which is pretty unpleasant.

What else officially supports Selenium (and, therefore, Geb)?

Whether to write browser tests?


The beauty of browser tests in the guaranteed behavior on a specific browser.

However, browser tests:
The simplest formula describing the need to write automated tests might look like this:



The formula is beautiful, but rather meaningless, as it is very difficult to estimate the complexity of the support and the cost of manual testing the iteration in advance. But it is clear that with an increase in the number of iterations, the idea to automate testing will occur to you more and more often. At a low speed of execution of browser tests, you can not pay much attention - it's still faster than performing manual testing. True, the formula does not take into account the fact that not all manual work can be automated - try, say, to explain to the robot what it means to “go to the layout”.

There are also subtleties in browser testing. For example, Selenium is not always able to independently determine whether a website that has been accessed by the browser is accessible at all. Indeed, in the case of, say, cutting down the Internet, the browser seems to be showing something . But this is not your expected page, but the internal message of the browser in the “check your Internet settings” style. All these situations you will have to define and include in the test package. In addition, sometimes you have to adapt the application for automatic tests, making some compromises.

In general, this may sound strange, but an automatic test is a program. Therefore, sometimes its development and support can be entrusted to programmers. This approach eliminates the strict division of labor between the programmer and the tester. As part of an established system for assembling and testing, the part applies the principle of “he himself broke the test himself and the order”. That is, roughly speaking, it does not matter who wrote the test - from the moment of its creation it becomes common property and both testers and programmers are responsible for the test.

We should also mention the so-called recording - the ability to record your actions in the browser and create tests on their basis. This feature is provided by Selenium IDE in Firefox. It sounds great, but tests created in this way usually have low stability , that is, the likelihood of them breaking due to some kind of page change is very high. This happens because recording does not know how to properly address the blocks of the page with which you interact - whether you need to use CSS classes, #id or some other methods. In addition, the logic (stuffing) of the Selenium tests for you still can not come up with.

I summarize: Geb is a pleasant to use and quite working product (we don’t really pay attention to the version number, for the stability of Geb is provided by Selenium), which is quite suitable for writing browser tests for Grails applications. Leaving you alone with him: http://gebish.org .

Source: https://habr.com/ru/post/139233/


All Articles