Thursday, September 25, 2014

Jenkins plugin for killing Selenium Grid

In this article I'd like to describe some basics of Jenkins CI plugins development.

Those who ever worked with Selenium Grid knows that sometimes it may stuck due to number of reasons. To minimize problems with test execution on stucked environment, we can create a simple plugin, that could shutdown hub / nodes. Of course environment killing wouldn't be enough to make our test execution process more stable. We would also need to think about some trigger to raise our configuration back. But let's start with simple things first...

Before coding, you'll need to make some preparations. Hope you already have Maven configured. To start developing Jenkins plugins, you need to add the following to .m2/settings.xml:


Now let's create a project skeleton with the following command: mvn -cpu hpi:create.
It will ask you several questions about group and artifact ids. For this example I left default groupId and set selenium-utils artifactId. After finishing with skeleton preparation, you'll see newly created selenium-utils folder with basic Jenkins plugin example that can be packaged to .hpi format (you can further upload it directly to Jenkins via plugin manager) or even deployed directly to test Jenkins instance. You can read more about project structure and common commands in the official tutorial.

Assuming you've already played with auto-generated basic Jenkins plugin, let's start developing our own, keeping in mind that our main goal is to create a trigger for killing Selenium Grid hub / nodes.

First you need to add the following dependencies into your pom.xml:


Selenium Grid provides us REST APIs we can use to shutdown hub / nodes, e.g. to kill hub we need to send the following GET request:

http://hubIp:port/selenium-server/driver?cmd=shutDownSeleniumServer 

Jersey client can help us to communicate with Grid. So let's create a simple client:


Actually, that's pretty much related to plugin's functionality. Now we only need to configure its layout and create appropriate handlers.

Let's start with resources folder: .jelly files are intended for creating form controls. Syntax is very similar to HMTL. Note: if you use IntelliJ IDEA, you can install Stapler plugin for .jelly syntax highlighting and IntelliSense support.

For this particular case we won't need global.jelly, so you can remove it. It could be required, if we needed to setup some global setting in the following Jenkins section:


config.jelly will introduce form components to be displayed for job's build action. Now let's think about what exact components we would need to achieve our main goal - killing hub / nodes. Generally, we should know only ip addresses and ports. Assuming we have 1 hub and N nodes we want to shutdown. For hub we would probably need only 2 textboxes. But what about nodes? If we say there could be N nodes' configurations, it's not enough to have just pure textbox components. We need something iterative. For this purpose we can use repatableProperty. So our config.jelly will look like the following:


In this part you should pay attention to field attributes, as soon we'll refer them in our java classes. Besides that, we see repeatableProperty component - something that will iteratively appear on a form, if we need more items. But this component should define some internal UI structure, e.g. the node ip / port textboxes. To define such structure we need to create another config.jelly, but it should be placed in a separate folder. So let's prepare our folders structure first.

Rename HelloWordBuilder to SeleniumBuilder and create new NodeConfiguration folder on the same level with SleniumBuilder. Now let's create config.jelly in NodeConfiguration directory with following repeatable content:


Besides common textboxes you can see 2 buttons for adding new / removing existing node configuration block. Currently we don't see any relation between these 2 jelly configs yet. It will appear only on class level.

We're almost ready with layout. As you may saw, there're some help files present besides configs. This is normaly some user-friendly description we see after clicking question mark against appropriate field. Note that these files should be named like help-fieldName, where fieldName should be exactly the same, as we created in our config.jelly. We'll skip this part.

Now it's time to create our plugin's engine, that will put everything together.

Important note: our resource config folders (SeleniumBuilder and NodeConfiguration) must be reflected via java classes. So we need to use exactly the same names for our java handlers.

Let's rename existing HelloWorldBuilder class to SeleniumBuilder. It will be our main plugin class. As you may see, it extends Builder class. It means that appropriate instance will be created when user selects our plugin while job's configuration.

All declared fields should match names defined in relative config.jelly. It'll let us to remember hub / nodes settings we put during job's configuration.

Constructor should be annotated with @DataBoundConstructor and list all declared instance variables as parameters, so that we can initialize them in its body. We would also need to create appropriate fields' getters.


When user triggers a build, perform method is invoked. At this point we сan easily access our saved configuration. Let's skip it and return later.

Our plugin should also contain an extension point. Generally it's a singleton, that defines some common plugin's configuration, such as display name, form validation rules, allows to load persisted global configuration, etc.


Some of you may notice that we missed one important part - nodes configuration. As we defined an appropriate config.jelly, we should also create its java handler. It will look almost the same, as SeleniumBuilder.

Let's create a new class NodeConfiguration in the same package, as SeleniumBuilder. It should also follow the same rules, but extend different classes.


If you remember from jelly configuration part, plugin's UI supports iterative nodes configuration block. It means that we should also create a list of NodeConfiguration and pass it to SeleniumBuilder's constructor. Note that it should still contain the same name, as we've defined in config.jelly for repeatableProperty.


Hope you've already added SeleniumClient into project. Let's finalize our plugin and add appropriate logic into perform method. To shutdown hub / nodes we just need to pass ip / port parameters we've read from plugin's UI.


That's pretty much it. Now let's test it: mvn hpi:run - it will run Jenkins instance with already deployed plugin.

If you open any job's configuration and expand Build section, you'll see our plugin in a list:


Let's click it. As you see, our plugin form contains exactly the same fields we've configured in jelly files. Validation prompts us to add some values.


Assuming you've already raised hub / nodes, let's fill in our fields and save a job.

Now if you trigger a new build and open console log, you would probably see the following:


If you want to deploy a plugin to your own Jenkins instance, you should run mvn install command first to create an .hpi file (will appear in your project's target folder), that you can then manually upload from Manage Plugins / Advanced section.

You can find full plugin's source on GitHub.

So what's next? Currently we have a mechanism for killing hub / nodes. And you would probably want to extend it by adding some new features, e.g. restart trigger, or implement even better solution. Take your time and play with a plugin. Good luck!

Saturday, September 6, 2014

MailCatcher REST client for emails testing

Sometimes it's important to verify emails content while web applications testing, or retrieve it to achieve some high level goals, e.g. password reset scenario. Besides that, we also need to keep in mind that customers shouldn't be spammed with test emails.

In this article we'll look at MailCatcher (MC) tool  - very simple SMTP server that is quite good for emails testing. Its key advantages are:
  • not spamming real users (it catches emails, preventing further sending to recipients list);
  • provides REST APIs for retrieving / deleting emails;
  • allows to read emails in json, html, plain text + full source;
  • has web UI;
  • cross-platform.
Note: you must install Ruby and SDK before setting up MC.
After MC installation, you'll be able to call mailcatcher from command line. It also has additional arguments, e.g. if you need to set custom ip or port:


Now we can open its web UI for accessing caught emails info:


So what's next? As I've mentioned above, MC provides some useful REST APIs for retrieving and deleting caught messages. It means that we can easily implement a simple REST client (e.g. via Jersey) for further content testing. Let's look at emails getting / deleting APIs:


You can find a full source code with some basic examples on GitHub.

Tuesday, September 2, 2014

Monitoring network activity via BrowserMob Proxy + HarStorage

Sometimes it's hard to understand a root cause of an issue while testing web applications, because bugs may not be as obvious to appear on UI. In such case it's important to pay attention to browser's network tab that can give us a lot of useful information about captured requests, e.g. internal server / JS errors.

We can capture network traffic automatically via BrowserMob Proxy that provides us useful REST API. After downloading and unzipping, you can start browsermob-proxy.bat to raise proxy server. Default port is 8080. You can change it by executing batch with -port argument.


Now we can use this proxy for listening traffic on a Selenium port. Generally you should only add an appropriate driver capabilities:


Where seleniumProxy configured to use BrowserMob proxy ip and allocated port:


To access BrowserMob proxy REST API, we can implement a simple REST client using Jersey. For example, to get a HAR file as String, we can send the following request:


You can find a link on a full source code below. And now let's focus on a second part of this article.

BrowserMob proxy allows us to retrieve HAR file and put it into stream. So we can easily save output json into file. It makes perfect sense when we need to run or debug just several tests. But what if we want to capture traffic for hundreds or thousands of tests? It would be much harder to find needed file. Besides that, we would also need to use some external tools to analyze generated HARs.

To make HAR files analysis more flexible, we can use HAR Storage. Just follow installation guide to raise the server for storing generated HAR statistics. By default it uses 5000 port.


To save generated by BrowserMob proxy HAR file into storage, we can use appropriate REST API:


If you run some tests and open HAR storage UI via browser, you can see the following user-friendly statistics:


You can find full source code with BrowserMob proxy and HAR Storage REST clients + simple Selenium test example on GitHub. Note that provided pom.xml already contains plugin for building a jar, if you want to add it into your project. Just exclude selenium / testng libraries and run clean install goals.