Showing posts with label Maven. Show all posts
Showing posts with label Maven. Show all posts

2015/04/01

Better Looking TestNG Reports with ReportNG

We had a set of system tests using Selenium 2 Webdriver and I was not satisfied with the default TestNG reports. I was  looking for a way to make reports look better and provide all the information necessary for analysis of a test failure when is happens. There were two requirements :
  1. provide nice, compact overview 
  2. include screenshot of a moment of failure
First thing I tried was Allure framework - it creates very nice reports but I had to reject it after some trials because it the way it works it was incompatible with the existing tests and its also quite invasive.

Fortunately I found ReportNG after that. The default design might not be so fancy but it is still very good and it fits well into TestNG and our tests.

First we had to add necessary dependencies to our maven POM:
<dependency>
   <groupId>org.testng</groupId>
   <artifactId>testng</artifactId>
   <version>6.8.8</version>
</dependency>

<dependency>
   <groupId>org.uncommons</groupId>
   <artifactId>reportng</artifactId>
   <version>1.1.4</version>
   <exclusions>
      <exclusion>
         <groupId>org.testng</groupId>
         <artifactId>testng</artifactId>
      </exclusion>
   </exclusions>
</dependency>

<dependency>
   <groupId>com.google.inject</groupId>
   <artifactId>guice</artifactId>
   <version>3.0</version>
</dependency>

TestNG has several interfaces to hook into the test processing, the most interesting probably are ITestListenerIConfigurationListener, and sometimes IMethodInterceptor. ReportNG add class HTMLReporter to that.

To add a screenshot to the report, we need to save it ITestListener.onContextFailure() and pick it up
in a custom ReportNGUtils -- for customization we need to provide custom Velocity context by overriding createContext() and passing custom ReportNGUtils implementation.


import org.apache.commons.io.FileUtils;
import org.apache.velocity.VelocityContext;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.testng.IConfigurationListener;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
import org.uncommons.reportng.HTMLReporter;

import java.io.File;
import java.net.URL;

public class TestListener extends HTMLReporter
       implements ITestListener, IConfigurationListener
{
    protected static final CustomReportNgUtils REPORT_NG_UTILS = new CustomReportNgUtils();

    @Override 
    protected VelocityContext createContext()
    {
        VelocityContext context = super.createContext();

        // VelocityContext has three properties: meta, utils, messages 
        // - see AbstractReporter.createContext()
        context.put("utils", REPORT_NG_UTILS);

        return context;
    }

    /** Invoked when test method (method with annotation @Test) fails. */
    @Override
    public void onTestFailure(ITestResult testResult)
    {
        if (getWebDriver(testResult) != null)
        {
            File scrFile = ((TakesScreenshot) getWebDriver(testResult))
                                             .getScreenshotAs(OutputType.FILE);
            String screenshotName = createScreenshotName(testResult);

            File targetFile = new File(screenshotName);
            FileUtils.copyFile(scrFile, targetFile); 
 
            URL scrUrl = new URL(getDriver(testResult).getCurrentUrl()); 
            Screenshot screenshot = new Screenshot(targetFile, srcUrl );
            testResult.setAttribute(Screenshot.KEY, screenshot);
        }

    }

    // ...
}

Class Screenshot is a custom class holding screenshot-related data, bare bones version could looke as this:

class Screenshot
{
    /* Name of {@link ITestResult} attribute for Screenshot. */
    static final String KEY = "screenshot";

    /** File in which is the screenshot stored. */
    File file;

    /** URL of a web application's page the screenshot captures. */
    URL url;
}

Now we need to add the custom ReportNGUtils implementation which picks up contextual information (Screenshot instance in our case) and uses it to modify the report output.

import java.util.List;

import org.testng.ITestResult;
import org.uncommons.reportng.ReportNGUtils;

class CustomReportNgUtils extends ReportNGUtils
{
    public List<String> getTestOutput(ITestResult testResult)
    {
        List<String> output = super.getTestOutput(testResult);

        if ( testResult.getAttribute(Screenshot.KEY) != null )
        {
            Screenshot screenshot = (Screenshot) testResult.getAttribute(Screenshot.KEY);
            String screenshotFileName = screenshot.getFile().getName();

            if (screenshot != null)
            {
                String url = (String) testResult.getAttribute("screenshotUrl");
                output.add(String.format("screenshot for %s  %s <br/><img src='../screenshots/%s'>",
                                         testResult.getName(), url, screenshotFileName)
                );
            }
        }

        return output;
    }
}

The final step is to register test listener in the plugin executing the tests. We use failsafe, the configuration for surefire is similar if you desire to use it.

<build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>

                <configuration>

                    <systemPropertyVariables>
                        <org.uncommons.reportng.escape-output> 
                        false 
                        </org.uncommons.reportng.escape-output>
                    </systemPropertyVariables>
                    <summaryFile>${project.build.directory}/failsafe-reports/failsafe-summary.xml</summaryFile>
                    <testClassesDirectory>${project.build.directory}/classes</testClassesDirectory> 
                    <properties>
                        <property>
                            <name>usedefaultlisteners</name>
                            <value>false</value>
                        </property>
                        <property>
                            <name>listener</name>
                            <value>
                                org.bithill.test.testng.TestListener
                            </value>
                        </property>
                    </properties>

                    <suiteXmlFiles>
                        <suiteXmlFile>src/main/resources/suiteX.xml</suiteXmlFile>
                    </suiteXmlFiles>

                </configuration>

                <executions>

                    <execution>
                        <id>integration-test</id> 
                        <phase>integration-test</phase>
                        <goals> <goal>integration-test</goal> </goals>
                    </execution>

                    <execution>
                        <id>verify</id> 
                        <phase>verify</phase>
                        <goals> <goal>verify</goal> </goals>
                    </execution>

                </executions>
            </plugin>

        </plugins>
    </build> 
 
And that's all - when you run 'mvn failsafe:integration-test' the tests are run, then you follow with 'mvn failsafe:verify',  which processes the results of the integration tests and generates a report and sets proper build result. Note that set the testClassesDirectory si crucial if you have it different than the expected 'test-classes'.

2015/03/28

Book Review: Mastering Apache Maven 3

When I got my hands on Mastering Apache Maven 3  by Prabath Siriwardena, I was sceptical at first, thinking it would be another half-cooked product trying to make up for lack of information with funny stories.

I am glad I can say that's not this case at all. The book is very good and covers all aspects of Maven in satisfactory level. Any developer who wants to us Maven efficiently will probably benefit from reading it and it useful also as comprehensive Maven reference.  Yes, as a natural nit-picker I found some things missing but it the number of such things was surprisingly  low.

What is even better it goes directly to the point, not wasting paper and my time on funny stories, jokes, or other filler stuff.


You will find in it description of POM (Project Object Model), various Maven configuration options, life cycle, description of several the most popular plug-ins, assemblies, archetypes, and repository management.  The final chapter about the best practices would have saved from some pain in past if I had it.

I will definitively find it place in my bookcase.

2012/02/23

Maven: Creating Executable JAR with Shade Plugin

I was pleasantly surprised by versatility of Maven shade plugin. Creating a custom-named executable JAR and bundle only the dependencies you really need is a piece of cake. The documentation is partly is online, partly in the plugin's help and provides all necessary information.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>1.5</version>
  <executions>
     <execution>
       <phase>package</phase>
       <goals><goal>shade</goal></goals>
       <configuration>
         <outputFile>target/tool.jar</outputFile>
         <artifactSet>
           <includes>
              <include>org.springframework:spring-core</include>
              <include>org.springframework:spring-beans</include>
           </includes>
         </artifactSet>
         <transformers>
           <transformer 
             implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
             <mainClass>org.bithill.example.Tool</mainClass>
           </transformer>
         </transformers>
       </configuration>
     </execution>
  </executions>
</plugin>

2011/12/05

Maven: The Aggregator vs. The Parent

I have recently realized that even professionals working with Maven for extended period do not fully grasp the differences of Maven's aggregators and parents. The reason for that is probably that both terms are related to multi-module projects and that both approaches often 'meet' in single file.

Multi-module projects usually have rather flat structure with a single top-level pom.xml file. That file than lists sub-modules and defines versions of dependencies and/or plugins inherited in these sub-modules. This is well-known thing - what is less known is that these roles of pom.xml can be  separated.

Aggregator
A top-level module serving for joining multiple modules under one roof is called aggregator. Its purpose is only represent more or less independently existing modules as a parts of a greater whole. 

Example of aggreator pom.xml:
<project xmlns="...">

<modelVersion>4.0.0</modelVersion>

<groupId>org.bithill</groupId>
<artifactId>aggregator</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<name>Project Aggregator</name>

<modules>
 <module>project1</module>
 <module>project2</module>
</modules>

</project>

Parent

As you see, aggegator does not include any information about dependencies. The source of to-be-inherited information about libraries and plugins is known as a parent POM. It includes all the properties, dependencyManagement and pluginManagement sections stating versions of projects dependencies and plugins and some plugin configurations when it comes handy.  Ideally this information should be de-duplicated and inherited by plugins is sub-modules, but that does not apply to reporting plugins.

Example of parent pom.xml:
<project xmlns="...">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.bithill</groupId>
    <artifactId>parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0</version>
    <name>shared parent</name>

    <properties>
      <java.version>1.6</java.version>
      <spring.version>3.0.2.RELEASE</spring.version>
    </properties>

    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
          <version>1.6.0</version>
        </dependency>

        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
          <version>${spring.version}</version>
        </dependency>
      </dependencies>
    </dependencyManagement>

    <build>
      <pluginManagement>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
              <source>${java.jdk.version}</source>
              <target>${java.jdk.version}</target>
            </configuration>
          </plugin>
        </plugins>
      </pluginManagement>
    </build>
</project>

Using Aggregator and Parent POM Together

So we showed that we can have two different artificial POM files serving two different roles - aggregation and inheritance.   


Diagram of relationships in a project consisting of two sub-modules:

The last missing thing in the picture is an example of sub-module's pom.xml. As you see, no dependency or build plugin need  to define their version - that is inherited from parent POM. Parent's pom.xml is deployed in Maven repository, but Maven's default relative path to parent is ".." -  to avoid aggregator being used as parent, property relativePath must be set empty, this is probably the only trick here.
<project xmlns="...">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.bithill</groupId>
    <artifactId>project1</artifactId>
    <packaging>pom</packaging>
    <version>1.6-SNAPSHOT</version>
    <name>Project #1</name>

    <parent>
      <groupId>org.bithill</groupId>
      <artifactId>parent</artifactId>
      <version>1.0</version>
      <relativePath/>
    </parent>
  
    <dependencies>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
      </dependency>
    </dependencies>

    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
        </plugin>
      </plugins>
    </build>

</project>

2011/11/20

Book Review: Apache Maven 3 Cookbook

I have decided to review new book about Maven from Packt Publishing: Apache Maven 3 Cookbook by Srirangan promising on its cover "Quick anwers to common problems".

The book is divided to nine chapters:
  1. basics
    Maven installation and environment settings. Generating, compiling and testing simple project. POM structure, build lifecycle and profiles.
  2. software engineering techniques
    Modularization, dependency management, static code analysis, JUnit, Selenium.
  3. agile team collaboration
    Nexus, Hudson, version control, offline mode.
  4. reporting and documentation
    Mvn site, javadocs, test and code quality reports, dahsboard.
  5. Java development
    Building and running web application (jetty), JEE, Spring, Hibernate, Seam.
  6. Google development
    Android, GWT, App Engine.
  7. Scala, Groovy and Flex
  8. IDE integration
    Eclipse, NetBeans, Intellij IDEA
  9. extending Maven
    plugin development basics
The book is certainly not material for beginners. Some terms are used without former definition or even explaining. Experienced Maven users will already know or will be able to find the missing pieces but beginners must be terribly confused. I think providing at least a description of Maven's standard directory layout for project or repository would be beneficial.

It is not so much about Maven as I expected, and says nothing about what is new in Maven 3. It should be exhaustive at least in the purely Maven parts, but even there is not enough information to make me happy. I would expect a description of template languages in the part dedicated to site plugin.

If you like puzzles and do not mind to use internet to search for missing pieces or you just cannot recall some setting covered in the book, you will like the book. It can also be used as a good starting point to show what tools, frameworks or languages can be used from/with Maven - do you like Maven and wanted to try GWT or Scala ? The book helps to lower entrance barrier by providing examples how to get working "playground" project in no time.

Would I buy it ? No. My bookcase has limited capacity and since the necessity to use internet to fill the gaps or correct bugs (although not many) in the book, I will be better with the online sources only.

2011/10/12

Java Applets - Building with Maven, Communicating with JavaScript

I have recently realized that I've not written any applet for some time and need to refresh my know-how. I hope writing it here will save some time for you if you are in similar situation.

The example will show the communication of Java Applet with Javascript in both ways -  Java-to-JS and JS-to-Java.

Building Applet with Maven

To help Maven to find applet classes and compile the applet, you need to add Java plugin dependency:
<dependency>
  <groupId>sun.plugin</groupId>
  <artifactId>plugin</artifactId>
  <version>1.6</version>
  <scope>system</scope>
  <systemPath>${java.home}/lib/plugin.jar</systemPath>
</dependency>

You'll also want to include content of your manifest file as the default genreated one does not contain Main-Class property/header:
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <configuration>
        <archive>
          <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
        </archive>
      </configuration>
    </plugin>
  </plugins>
</build>

Example of the manifest:
Manifest-Version: 1.0
Main-Class: org.bithill.SimpleApplet

Deployment

One of things that changed significantly since writing my last applet  is an introduction of the deployment toolkit - a JavaScript library for including an applet into a web page. It looks like a marvel when compared to the tedious and error-prone applet or object tag creation and checking of the browser differences.

<script src="deployJava.js"></script>
<script>
  var attributes =
  {
    id: 'simpleApplet',
    codebase:'../../../target', // directory with the jar
    code:'org.bithill.SimpleApplet.class',
    archive:'SimpleApplet-1.0.jar',
    width: 100, height: 50,
    boxbgcolor: '#eeeeee'
  };
  var parameters = {};
  var version = '1.6';

  deployJava.runApplet(attributes, parameters, version);
</script> 
The Applet and Its Iteraction with JavaScript
package org.bithill;

import static java.lang.System.out;
import netscape.javascript.JSException;
import netscape.javascript.JSObject;
import java.applet.Applet;
import java.util.Date;

public class SimpleApplet extends Applet
{
   private JSObject js; // object for communication with JavaScript

   /** Evaluates given JavaScript expression.
    * @param jsExpression expression
    */
   public String jsEval(String jsExpression)
   {
      try { js.eval(jsExpression); }
      catch (JSException ex) { ex.printStackTrace();  }
      return new Date() + " | " + jsExpression;
   }

    /** Initializes the applet.
     *  It's called only once before the applet is started.
     */
    @Override
    public void init()
    {
       try { js = JSObject.getWindow(this);  }
       catch (JSException ex) { ex.printStackTrace();  }
    }
}

The applet gets reference to JSObject for interacting with JavaScript engine instance in the page. It is then used in jsEval() method which also return time-stamped input to demonstrate reading of the method's rerurn value. In page you need only several lines of JavaScript to connect the things together:

<script>function getAlertExpression(msg) { return "alert('" + msg + "')"; }</script>

<button onclick="document.getElementById('msgbox').innerText = ( simpleApplet.jsEval( getAlertExpression('HI') ) )">
show alert
</button>

<div id="msgbox">-- last action --</div> 

How does it work? The button has JavaScript onclick handler that calls the applet's the jsEval() method and puts its retrurn value to prepared msgbox div. Notice that applet's id is used as reference. Method jsEval() evalueates JavaScript expression in the page -that results in showing an alert dialog.

2011/06/22

Fighting Crawling Maven Dependency Changes With Git

You know the situation well - everything goes fine, continuous integration does what is used to do and new releases are produced on regular basis.

Then, out of the blue, integration tests fail with Java complaining about two different versions of the same library on classpath. You can be less lucky and use different version than you think, or different implementation of an API, or your JAR/WAR/whatever-package just grows without reason. Lot of bad things can happen when dependecies get out of control.

It does not depend on the source - one of your colleagues changing something without double-check or changes in some already used library. To fight these problems I tried to use Git pre-commit hook you can see below. It is not perfect but it works. It depends on Maven, if you use Ivy, Gradle or something else, you will have to adapt it.

Just put it in you projects' /git/hooks named pre-commit. When dependency changes are detected, they are printed and commit is aborted - you will have to use diff tool on deps.txt (old dependencies) and deps_tmp.txt (new dependencies) and update deps.txt if the dependency change is desired. If the change is unwanted, use mvn dependency:tree to find the source of problems.

It has only one more constraint -- all people using it must use the same version of Maven. Because of changes in dependency resolution in Maven 3,  Maven dependency plug-in produces slightly different list of changes in versions 2 and 3.

#!/bin/sh

mvn dependency:list -DincludeScope=compile | sort | grep jar \
| cut -c 11- | cut -d":" -f 1-4 | uniq >deps_tmp.txt

diff -a -B -b -u deps.txt deps_tmp.txt >deps_diff.txt

lines=`cat deps_diff.txt | wc -l`

if [ $lines -ne 0 ]; then
   echo "Dependency change detected - check 'mvn dependency:tree' and update deps.txt."
   cat deps_diff.txt
   if [ -a deps_diff.txt ]; then rm -f deps_diff.txt; fi
   exit 1
fi