Saturday, October 12, 2013

Writing Selenium Tests in a more DSL like way

I noted in a previous blog post that I was starting to write more tests in a more fluent DSL like manner. I thought I would illustrate this. Here is an example of a test that does the following (assuming you are logged in on the home screen):

  1. From the navigator (think of it as a button bar) click the "Create wantlist" - this will launch a create wantlist editor and return this solvent.
  2. We want to fill in the fields and check for certain conditions (like if the form is valid or not). 
  3. We want to hit "save" button
I do not show the additional steps of verifying the item that got created in the table.

public void create() {
    Navigation nav = new Navigation(config);
    WantlistEditor editor = nav.create(Navigation.CreateAction.WANTLIST);
    String id = generateUnique("");
    editor.invalid()
            .set(factory.get(StampAttribute.Country, Country.BASUTOLAND))
            .set(factory.get(StampAttribute.Denomination, "2d"))
            .set(factory.get(StampAttribute.Description, "scarlet (\"Tail\" flaw)"))
            .set(factory.get(StampAttribute.Catalogue, Catalogue.STANLEY_GIBBONS))
            .set(factory.get(StampAttribute.CatalogueCondition, Condition.MINT_NG))
            .set(factory.get(StampAttribute.CatalogueNumber, "193a-" + id))
            .set(factory.get(StampAttribute.CatalogueValue, 2.25))
            .valid()
            .save()
            .success();



On thing to point out is that when I call a method like valid() or invalid() I am not returning a boolean from the function (in the past it would have been a isValid() type syntax. Instead, I am coding the test as I expect it to behave and if it is not behaving in this manner it will fail at that point. As well a method like success() is programmed to look for the closure of the editor as marking a successful create.

As mentioned earlier, I am using an attribute factory to get the attribute. An attribute is a nifty class I created which is able to hold a value that was set (so I can verify it) and also state what the input type is (selection, checkbox, text etc.) and is able to return a selenium By object that I can use for selection.

On large disadvantage of using the chaining approach is exceptions. Unless your methods themselves do not report errors well, it is difficult to trace what line causes the error. This is one disadvantage of DSL interfaces and means you need to think better about the erroneous conditions and how best to log/print them to console/log files.

Selecting ChosenJS values with Selenium

I had previously blogged about selection of GXT selections with selenium and figured it might be worthwhile if I posted what I was doing with my new AngularJS app that is using ChosenJS as my selection of choice (I wrapped this in a simple directive)

For my selenium tests, I try and write most of the code in DSL format in a object termed a Solvent.  We use this term at my place of employment and I think it is a good term and have continued to use it.  This allows me to encapsulate the underlying implementation while presenting a functional level to the selenium tests.  For form controls, I use a generic Attribute class that along with an AttributeHandler knows how to process any attribute type (for example, checkboxes, textfields, textareas, selections, date pickets etc.).  Thus we come to the "code" of how I handle ChosenJS Selections.

First off, lets look at what the generated HTML looks like for the AngularJS control (this represents a drop-down for my stamp conditions):


<select class="condition-selector ng-pristine ng-valid ng-valid-required" name="condition" data-jq-chosen data-options="conditions" data-placeholder="Select a condition" data-ng-model="model.activeCatalogueNumber.condition" data-ng-required="true" data-ng-change="validate()" tabindex="-1" required="required" style="display: none;">
 <option value="? string:0 ?"></option>
 <!-- ngRepeat: cond in conditions -->
 <option data-ng-repeat="cond in conditions" value="0" class="ng-scope ng-binding">Mint</option>
 <option data-ng-repeat="cond in conditions" value="1" class="ng-scope ng-binding">Mint (NH)</option>
 <option data-ng-repeat="cond in conditions" value="2" class="ng-scope ng-binding">Used</option>
 <option data-ng-repeat="cond in conditions" value="3" class="ng-scope ng-binding">Cancel to Order</option>
 <option data-ng-repeat="cond in conditions" value="4" class="ng-scope ng-binding">Mint No Gum</option>
</select>


Mostly this is Angular, although you may notice the data-jq-chosen and data-options variables which are instructions/directives for my directive (one indicates it should turn this drop-down into a ChosenJS control and the other that it should watch/bind to the conditions variable on the controller). If you are not using AngularJS this probably doesn't mean to much to you.
If ChosenJS was not present we could simply operate on this HTML and use an element selector like By.ByName("condition-selector") and click it and choose the appropriate <option> tag matching the value desired. But when ChosenJS is present, it will add additional HTML into the DOM. Specifically for the above example we have this:

<div class="chosen-container chosen-container-single chosen-container-single-nosearch" style="width: 121px;" title="">
 <a class="chosen-single chosen-single-with-deselect" tabindex="-1"><span>Mint</span><abbr class="search-choice-close"></abbr></a>
 <div class="chosen-drop">
  <div class="chosen-search"><input type="text" autocomplete="off" readonly="" tabindex="21"></div>
  <ul class="chosen-results">
   <li class="active-result result-selected ng-scope ng-binding" data-option-array-index="1" style="">Mint</li>
   <li class="active-result ng-scope ng-binding" data-option-array-index="2" style="">Mint (NH)</li>
   <li class="active-result ng-scope ng-binding" data-option-array-index="3" style="">Used</li>
   <li class="active-result ng-scope ng-binding" data-option-array-index="4" style="">Cancel to Order</li>
   <li class="active-result ng-scope ng-binding" data-option-array-index="5" style="">Mint No Gum</li>
  </ul>
 </div>
</div>


And it is this fragment that we need to interact with (since the chosen-container floats on top of the select DOM element). This ChosenJS DOM will always appear as a peer to the select DOM elements. I order to get this to work in Selenium we need to:

  1. Click the chosen-container to invoke the drop-down (this is unpopulated until shown for performance reasons)
  2. Click the appropriate li element.
How I do this, is with some code in my AttributeHandler and when I do a swtich statement on the control type, the logic I have for the select control (which in my case are all ChosenJS) looks like the following:

case Select:
  elm = parent.findElement(attr.asBy());
 String chosen = "../div[contains(@class,'chosen-container')]";
 elm.findElement(SolventHelper.ngWait(By.xpath(chosen + "/a"))).click();
 elm.findElement(SolventHelper.ngWait(By.xpath(chosen + "/div[contains(@class,'chosen-drop')]/ul/li[.=\"" + attr.getValue() + "\"]"))).click();
 assertEquals(attr.getValue(), elm.findElement(SolventHelper.ngWait(By.xpath(chosen + "/a"))).getText());
 break;


In the code fragment above, the parent is passed to the AttributeHandler as a WebElement. This is the container where that form element is located. It could be a fieldset, a panel div or a popup window etc. It is just a starting point so I can find the appropriate element in close proximity. The ngWait( ) method comes from the Protractor project (obviously it is a Java version of this) and simply waits for Angular to complete its digest (The fluent angular project uses this and you can see the source here.)

You may also notice I do an assertion check just to ensure that the value got set on the link. I may ultimately remove this.

But wait, I see you are using xpaths.... that is so last year.... You are right. CSS Selectors is definitely a better way to go. Except.... there is no CSS selector for parent. Often I have found I am starting to use a more mix of CSS Selectors and XPaths. I suppose I could have gotten a Web Element for the "chosen" variable and then simply used CSS selectors from that. Overall, what is nice about this, is I have yet to run into a problem with timing issues like I had with GXT and EXT-JS selectors. Also, the li tags for the values will be ALL the values in a scrollable window, so selenium will select the right value even if it is not on the screen.

I have been a little slow putting together a regression suite in Selenium for my redesigned Stamp Web application written in AngularJS, but I want to upgrade to 1.2.0 and in order to do so wanted to have about 15-20 critical test cases covered first. So far my experiences has been very positive and the number of timing issues is almost negligible (the only issue I have had is when I launch the application I maximize it and sometimes the app will start the login before maximized which causes some issues). Other than that, I have yet to have to use a single "pause()" method which is really nice.




Thursday, October 3, 2013

Arquillian with Glassfish 4 - early trials

With the release of Glassfish 4 and J2EE version 7, I decided to work at upgrading my stamp web services application to the platform. An important part of my project is my 580 odd tests that include approximately 300 or so Arquillian tests. Since my application takes advantage of Container Managed transaction context and persistence providers, it is not sufficient for me to simply tests these in a out-of-container context. Afterall, how can I verify that listeners or cascading relationships cleanup correctly on a delete operation preventing orphans?

Problem with Latest Arquillian


In making the shift to Glassfish 4, I also upgraded the version of Arquillian and ShrinkWrap I was using and ran into essentially classpath hell. I figured my adventures were sufficient that I would blog about it.

First off, once I had it all working, I ran into a bug under Arquillian 1.1.1.Final. The exception was

WELD-001456 Argument resolvedBean must not be null

It turns out, there is an existing bug for this: . In order to workaround this bug, I needed to use Arquillian 1.0.3.Final.  Hopefully this will get fixed soon and I can move up.

Updates to pom.xml


So moving to what I changed (that is why you came here right?) lets start with the pom.xml. 

  1. Since J2EE 7 uses a pretty recent version of Jersey for JAX-RS compliance, I modified my included Jersey dependencies from compile to provided. (The exception was jersey-client used in my tests that I might not need anymore with the latest JSR specs). I tied the version to the version in Glassfish 4 for compatibility (1.17)
  2. I didn't think I would need eclipselink when testing with the embedded glassfish, but this turned out to be needed.  So for this I used the 2.5.0 version matching Glassfish 4.  As well, since I do not have a compile time dependency I changed the scope from provided to runtime.

          <dependency>
                <groupId>org.eclipse.persistence</groupId>
                <artifactId>eclipselink</artifactId>
                <version>${eclipselink.version}</version>
                <exclusions>
                    <exclusion>
                       <artifactId>commonj.sdo</artifactId>
                        <groupId>commonj.sdo</groupId>
                    </exclusion>
                </exclusions>
                <scope>runtime</scope>
            </dependency>

  3. I was previously including the dependency org.eclipse.persistence.moxy to support MOXy in my application, but since this is the default provider now for Glassfish 4 JAXB processing (for JSON) I was able to remove it.
  4. For the glassfish-embedded-all artifact, I switched to use 4.0.
  5. Finally, in the surefire-plugin configuration, I had some classpath dependency exclusions. I had to add CDI to this or else Arquillian was bombing out (one of the JARs was bringing in a incompatible version of cdi-api). I found this using the netbeans maven plugin and the graphical display and this really helps. For reference here is the exclusions:
    
    <classpathDependencyExcludes>
      <classpathDependencyExcludes>javax.servlet:servlet-api</classpathDependencyExcludes>
      <classpathDependencyExcludes>org.apache.felix:javax.servlet</classpathDependencyExcludes>
      <classpathDependencyExcludes>javax:javaee-web-api</classpathDependencyExcludes>
      <classpathDependencyExcludes>javax.enterprise:cdi-api</classpathDependencyExcludes>
      <classpathDependencyExcludes>org.jboss.spec:jboss-javaee-web-6.0</classpathDependencyExcludes>
    </classpathDependencyExcludes>
    
  6. Updated the version of ShrinkWrap to 2.0.0


I would provide the full pom.xml, but it is quite large (currently 508 lines) so I only included these snippets (also pygments.org is not up so I can not get nicely formatted code) .

Summary


Overall, this was a little more painful than I expected - although most of the pain came in the changes to ShrinkWrap and Arquillian versions that I thought I needed for GF4.   What compounded this was the sneaky inappropriate version of CDI that was causing all the dependency injection to fail.