September 9, 2012

Allowing JUnit Tests to Pass Test Case on Failures


Why create a mechanism to expect a test failure?

There comes a time when one would want and expect a JUnit @Test case fail. Though this is pretty rare, it happens. I had the need to detect when a JUnit Test fails and then, if expected, to pass instead of fail. The specific case was that I was testing a piece of code that could throw an Assert error inside of a call of the object. The code was written to be an enhancement to the popular new Fest Assertions framework, so in order to test the functionality, one would expect test cases to fail on purpose.


A Solution

Jump to the Code
One possible solution is to utilize the functionality provided by a JUnit @Rule in conjunction with a custom marker in the form of an annotation.


Why use a @Rule?

@Rule objects provide an AOP-like interface to a test class and each test cases. Rules are reset prior to each test case being run and they expose the workings of the test case in the style of an @Around AspectJ advice would.

Required code elements

  • @Rule object to check the status of each @Test case
  • @ExpectedFailure custom marker annotation
  • Test cases proving code works!
  • Optional specific exception to be thrown if annotated test case does not fail

NOTE: working code is available on my github page and has been added to Maven Central. Feel free to Fork the project and submit a pull request

Maven Usage

<dependency>
    <groupId>com.clickconcepts.junit</groupId>
    <artifactId>expected-failure</artifactId>
    <version>0.0.9</version>
</dependency>

Example Usage

In this example, the "exception" object is a Fest assertion enhanced ExpectedException (look for my next post to expose this functionality). The expected exception will make assertions and in order to test those, the test case must be marked as @ExpectedFailure

public class ExceptionAssertTest {

    @Rule
    public ExpectedException exception = ExpectedException.none();

    @Rule
    public ExpectedTestFailureWatcher watcher = ExpectedTestFailureWatcher.instance();

    @Test
    @ExpectedFailure("The matcher should fail becasue exception is not a SimpleException")
    public void assertSimpleExceptionAssert_exceptionIsOfType() {
        // expected exception will be of type "SimpleException"
        exception.instanceOf(SimpleException.class);
        // throw something other than SimpleException...expect failure
        throw new RuntimeException("this is an exception");
    }
}

Implementation of Solution

Reminder, the latest code is available on my github page.

@Rule code (ExpectedTestFailureWatcher.java)

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
// YEAH Guava!!
import static com.google.common.base.Strings.isNullOrEmpty;

public class ExpectedTestFailureWatcher implements TestRule {

    /**
     * Static factory to an instance of this watcher
     *
     * @return New instance of this watcher
     */
    public static ExpectedTestFailureWatcher instance() {
        return new ExpectedTestFailureWatcher();
    }

    @Override
    public Statement apply(final Statement base, final Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                boolean expectedToFail = description.getAnnotation(ExpectedFailure.class) != null;
                boolean failed = false;
                try {
                    // allow test case to execute
                    base.evaluate();
                } catch (Throwable exception) {
                    failed = true;
                    if (!expectedToFail) {
                        throw exception; // did not expect to fail and failed...fail
                    }
                }
                // placed outside of catch
                if (expectedToFail && !failed) {
                    throw new ExpectedTestFailureException(getUnFulfilledFailedMessage(description));
                }
            }

            /**
             * Extracts detailed message about why test failed
             * @param description
             * @return
             */
            private String getUnFulfilledFailedMessage(Description description) {
                String reason = null;
                if (description.getAnnotation(ExpectedFailure.class) != null) {
                    reason = description.getAnnotation(ExpectedFailure.class).reason();
                }
                if (isNullOrEmpty(reason)) {
                    reason = "Should have failed but didn't";
                }
                return reason;
            }
        };
    }
}

@ExpectedFailure custom annotation (ExpectedFailure.java)

import java.lang.annotation.*;

/**
 * Initially this is just a marker annotation to be used by a JUnit4 Test case in conjunction
 * with ExpectedTestFailure @Rule to indicate that a test is supposed to be failing
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
public @interface ExpectedFailure {
    // TODO: enhance by adding specific information about what type of failure expected
    //Class assertType() default Throwable.class;

    /**
     * Text based reason for marking test as ExpectedFailure
     * @return String
     */
    String reason() default "";
}

Custom Exception (Optional, you can easily just throw RuntimeException or existing custom exception)

public class ExpectedTestFailureException extends Throwable {
    public ExpectedTestFailureException(String message) {
        super(message);
    }
}

Can't one exploit the ability to mark a failure as expected?

With great power comes great responsibility, it is advised that you do not mark a test as being @ExpectedFailure if you do not understand exactly why the test if failing. It is recommended that this testing method be implemented with care. DO NOT use the @ExpectedFailure annotation as an alternative to @Ignore

Possible future enhancements could include ways to specify the specific assertion or the specific message asserted during the test case execution.

Known issues

In this current state, the @ExpectedFailure annotation can cover up additional assertions and until the future enhancements have been put into place, it is advised to use this methodology wisely.

March 19, 2012

Maven 3 Site Docs Part 1: Basic Site Documentation

What is documentation?

Documentation
The process of documenting knowledge

Documentation for software developers are documents that describe how to run or use the artifact/project. Typically documentation provides any exposed settings, their uses and default values. In addition information about integration, entry and exit points such as JMS queues, JMX MBeans, DataSource objects and/or JNDI variables in order to run and use the artifact. There are many programs and standards to produce this level of documentation including MS word, Sharepoint, doxygen, Latex, Wiki, markdown, textile, etc.

Outside of artifact specific documentation team leads, architects and project managers usually ask for JavaDocs to be included. JavaDocs, if combined with Maven provide more documentation for your IDE and better information for the consumers of your artifact. Other important documentation includes testing reports, test coverage, static analysis tools, dependencies of your artifact, change log and issue management such as Jira or Bugzilla.

Why create docs?

The best reason to care about documentation is to provide information for external developers and team members outside of the project's development environment. Documentation can be utilized inside of IDEs as well and face it, how many times do you wish you had sources and/or JavaDocs for a dependency when you were programming or to send back an email with the site docs so someone can be self-served rather than take your time.

Problems with traditional documentation

The largest complaint with traditional documentation is that it becomes stale over time. Time is never allocated in a project&dagger to provide or update documentation and stale docs can lead a developer or team down many wrong paths leading to hours or days of misinformation lead rabbit holes.

† Agile, SCRUM and Lean processes should allow time to be built-in to handle technical debt like documentation, even thought it is called the "necessary evil". Read more here

Secondly, developers are the ones who are askedforced to maintain the documentation and typically developers are not great in the writing department and unless segments of the documentation process are automated, developers are people and we forget.

Last major problem with traditional documentation is that it is typically not automated and is an extra step during the build/release cycle. Any process in the build cycle that is not automated and tested will break down at some point. Humans are great at abstract thinking, not repetitive processes.

Solution to the problems

As stated above, the main problems are developers maintaining all documentation and documentation that is either built outside of the immediate environment or is not automated. This is where Maven's site documentation comes in. Maven was built with the understanding that documentation needs to be automated and has been progressively improved throughout updates to the maven project.

The most common case for Maven site docs is to deploy generated documentation to an external web server via SCP, FTP, WebDAV or SFTP. The site plugin takes use of Apache's Wagon library for secure transfer of docs. Most of the time the site docs are generated during the release† or deploy phases and generate on the verify phase.

† Release isn't a phase in maven, it is a plugin, but it is considered a best practice when publishing versioned artifacts to a maven repository

Maven 3 vs Maven 2

Maven decided to change the way that reports were collected and generated in between the Maven 2 and Maven 3 releases. In Maven 2, the POM would have a section called >reports< where plugins that handled ReportSet would be handled. In Maven 3, there is now a plugin that handles and collates the ReportSets instead of Maven handling it. The plugin is called maven-project-info-reports-plugin and documentation can be found here.

Sources for this series of blog post

I have provided a link to a fully functional multi-module maven project that publishes to both http://mike-ensor.github.com/multi-module-site-doc-project/index.html and locally to ${env.HOME}/sitedocs/${project.artifactId}.

https://github.com/mike-ensor/multi-module-site-doc-project.

Maven 3 Site Docs

There are two important elements with respect to site docs for Maven. Plugins, which generate ReportSet elements and Site Doc/Descriptors.

Plugins

Plugins are an important part of the documentation process and will typically run throughout the maven build lifecycle generating artifacts for the plugin phase that generates the ReportSet. A good example would be the maven-checkstyle-plugin. The plugin is typically bound to the process-resources or packakge phases so that developers know they have a checkstyle violation early in the build lifecycle. The plugin generates a checkstyle.xml document that is then used in the site phase by the maven-project-info-reports-plugin to generate a checkstyle report page.

Maven Site Plugin

The maven-site-plugin is the main plugin that is used to crate all site documentation. Each plugin that produces and/or consumes a ReportSet will need to be configured inside of the <reportPlugins> section. NOTE: This is where the Wagon plugin is set as a dependency. The Site plugin will use the <distributionManagement><site> line to determine where the site docs are to be published (SCM, WebDAV, SFTP, etc)

<plugin>
 <artifactId>maven-site-plugin</artifactId>
 <version>${plugin.maven-site.version}</version>
 <dependencies>
  <dependency><!-- add support for ssh/scp -->
   <groupId>org.apache.maven.wagon</groupId>
   <artifactId>wagon-ssh</artifactId>
   <version>${plugin.wagon-ssh.version}</version>
  </dependency>
 </dependencies>
 <configuration>
  <attach>true</attach>
  <reportPlugins>
   <plugin>
   ...
   ... Plugins go here
   ....
   </plugin>
  </reportPlugins>
  <attach>true</attach>
 </configuration>
</plugin>

Maven Project Info Report Plugin

The maven-project-info-report-plugin is the main plugin used by the maven-site-plugin that Maven 3 utilizes to collate and produce the common site reporting docs for the artifact

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-project-info-reports-plugin</artifactId>
    <configuration>
        <dependencyDetailsEnabled>false</dependencyDetailsEnabled>
        <dependencyLocationsEnabled>false</dependencyLocationsEnabled>
    </configuration>
    <!-- simpler configuration without
        reportSets available for usual cases -->
    <!-- distribution-management,
        index, dependencies, help,
        issue-tracking, plugins,
        cim, license, dependency-management,
        mailing-list, project-team,
        dependency-convergence,
        scm, plugin-management,
        modules, summary -->
    <reports>
        <report>summary</report>
        <report>dependencies</report>
        <report>project-team</report>
        <report>issue-tracking</report>
        <report>scm</report>
        <report>cim</report>
        <report>modules</report>
        <report>plugins</report>
    </reports>
</plugin>

Take a quick look at the types of reports that are automatically generated using the maven-project-info-reports-plugin. Many of these automatically generated reports provide additional information on top of the redundant information provided by the below listed plugins.

List of automatically provided reports
  • distribution-management
  • index
  • dependencies
  • help
  • issue-tracking
  • plugins
  • cim
  • license
  • dependency-management
  • mailing-list
  • project-team
  • dependency-convergence
  • scm
  • plugin-management
  • modules
  • summary

Common Plugins

Checkstyle - maven-checkstyle-plugin – Provides static analysis on source files – NOTE: Checkstyle needs to be run during build AND report phases

Example report plugin

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-checkstyle-plugin</artifactId>
 <configuration>
  <skip>${checkstyle.skip}</skip>
  <configLocation>${checkstyle.configUrl}</configLocation>
  <failsOnError>false</failsOnError>
  <enableRulesSummary>true</enableRulesSummary>
  <includeTestSourceDirectory>true</includeTestSourceDirectory>
 </configuration>
</plugin>

NOTE: A best practice is to provide a flag to enable/disable the plugin on a project-by-project basis. Also, notice the failsOnError, this will ensure that the plugin will produce an artifact on error and present that to the user.

FindBugs - maven-findbugs-plugin – Provides static analysis on source files – Plugin does not need to be run outside of report phase

Example report plugin

<plugin>
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>findbugs-maven-plugin</artifactId>
 <configuration>
  <skip>${findbugs.skip}</skip>
  <xmlOutput>true</xmlOutput>
 </configuration>
</plugin>

NOTE: A best practice is to provide a flag to enable/disable the plugin on a project-by-project basis. Also, notice the failsOnError, this will ensure that the plugin will produce an artifact on error and present that to the user.

Cobertura - maven-cobertura-plugin – Provides code coverage during Unit Test (test) phase. NOTE: Must be run during build AND during reporting phases

Example report plugin

<plugin>
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>cobertura-maven-plugin</artifactId>
 <configuration>
  <skip>${cobertura.skip}</skip>
 </configuration>
</plugin>

NOTE: A best practice is to provide a flag to enable/disable the plugin on a project-by-project basis. Also, notice the failsOnError, this will ensure that the plugin will produce an artifact on error and present that to the user.

PMD and CPD - maven-pmd-plugin – Provides static analysis (PMD) and code duplication/cut-copy-paste detection for artifact. Read more about the PMD plugin here.

Example report plugin

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-pmd-plugin</artifactId>
 <configuration>
  <skip>${pmd.skip}</skip>
  <targetJdk>${maven.compiler.source}</targetJdk>
  <sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
  <minimumTokens>100</minimumTokens>
  <rulesets>
   <ruleset>${maven-pmd-plugin.configLocation}</ruleset>
  </rulesets>
  <failOnViolation>false</failOnViolation>
 </configuration>
</plugin>

Surefire and Failsafe - maven-surefire-report-plugin – Creates reports based on tests cases for unit tests and optionally integration tests. NOTE, This is a different plugin than the maven-surefire-plugin, this is the report plugin that operates on the ResultSet for both Surefire and Failsafe. Both Surefire and Failsafe must be run during the build for this plugin to generate reports.

Example report plugin

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-report-plugin</artifactId>
    <reportSets>
        <reportSet>
            <id>integration-tests</id>
            <reports>
                <report>report-only</report>
                <report>failsafe-report-only</report>
            </reports>
        </reportSet>
    </reportSets>
</plugin>

JavaDoc - maven-javadoc-plugin – Bundles JavaDocs into the reporting structure. NOTE, this does not need to be run during the standard build, only during the reporting phase

Example report plugin

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-javadoc-plugin</artifactId>
    <configuration>
        <skip>${javadocs.skip}</skip>
        <failOnError>false</failOnError>
    </configuration>
</plugin>

JXR - maven-jx-plugin – Builds cross-link information for classes. Checkstyle, PMD, FindBugs, JavaDocs and various other plugins can utilize the JXR configuration. NOTE, this plugin must be run at an early place in the Maven lifecycle (suggest process-resources) in addition to the sitedoc plugin section.

Example report plugin

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jxr-plugin</artifactId>
    <configuration>
        <aggregate>true</aggregate>
    </configuration>
</plugin>

Useful Plugins

Other handy plugins to use for site documentation include:

End of Part 1

The next post will cover the developer portion of the documentation; more specifically covering XDoc and APT. The site documentation is robust and the next post will show how to program some of the XDoc portions of the docs by leveraging Velocity templates.

The last part of this blog post will cover multi-module projects. In addition, that post will show how to deploy site-docs to a Github Pages so you can have site docs at http://<username>.github.com/<your-project>.

February 21, 2012

Colorized Maven Output on Mac OSX

Overview

Unlike many other open source build tools (for example, git) Maven does not provide any type of colorized output. Maven's excessive amount of output can be hard to read and you tend to get lost in the sea of text. My current client I've been given a Mac to use and quickly found out that the existing Linux scripts did not work with iTerm. My solution, take the existing Linux scripts and modify for the Mac OSX.

Approach

Since Maven does not have colorized output, the only option is to use "sed" (stream editor) that takes the output of maven, matches strings with regex and then adds in color codes. The terminal does not escape ansi colors during the stream editing phase. The solution is to use the "tput" command along with setab and setaf for the background and foreground respectively.

Example of ANSI colors

echo -e "\033[1;34RED TEXT\[0m"

Example of TPUT colors

echo -e "${`tput setaf 1`}RED TEXT${`tput sgr0`}"

The Script

#!/bin/sh

# Written by Mike Ensor (mike@ensor.cc)
# Copywrite 2012
# Use as needed, modify, have fun!
# This is intended to be used for Maven3 + Mac OSX
#
# To use:
# in your ".bashrc" or ".bash_profile" add the following line:
# source ~//colorize-maven.sh
#

#Predefine all variables
c_black=
c_cyan=
c_magenta=
c_red=
c_white=
c_green=
c_yellow=
c_blue=
c_bg_black=
c_bg_cyan=
c_bg_magenta=
c_bg_red=
c_bg_white=
c_bg_green=
c_bg_yellow=
c_bg_blue=
c_end=
c_bold=

xterm_color() {
 # 0 Black
 # 1 Red
 # 2 Green
 # 3 Yellow
 # 4 Blue
 # 5 Magenta
 # 6 Cyan
 # 7 White

    # Yes, this could be a map
    c_bold=`tput setaf 0`
    c_bg_bold=`tput setab 0`
    c_black=`tput setab 0`
    c_bg_black=`tput setab 0`
    c_cyan=`tput setaf 6`
    c_bg_cyan=`tput setab 6`
    c_magenta=`tput setaf 5`
    c_bg_magenta=`tput setab 5`
    c_red=`tput setaf 1`
    c_bg_red=`tput setab 1`
    c_white=`tput setaf 7`
    c_bg_white=`tput setab 7`
    c_green=`tput setaf 2`
    c_bg_green=`tput setab 2`
    c_yellow=`tput setaf 3`
    c_bg_yellow=`tput setab 3`
    c_blue=`tput setaf 4`
    c_bg_blue=`tput setab 4`
    c_end=`tput sgr0`
}

#This is not used (yet)
ansi_color() {
    c_bold=   '[1m'
    c_blue=   '[1;34m'
    c_black=  '[1;30m'
    c_green=  '[1;32m'
    c_magenta='[1;35m'
    c_red=    '[1;31m'
    c_cyan=   '[1;36m'
    c_end=    '[0m'
}

color_maven() {

    # pick color type
    if [ $TERM = 'xterm-color' ]
    then
        xterm_color
#    elif [ $TERM = 'ansi' ]
#    then
#     ansi_color
 else
     echo "${c_red}WARNING:::Terminal '${TERM}' is not supported at this time. Colorized output will not happen for Maven${c_end}"
 fi

 error=${c_bold}${c_red}
 info=${c_white}
 warn=${c_yellow}
 success=${c_green}
 projectname=${c_bold}${c_cyan}
 skipped=${c_white}
 downloading=${c_magenta}

 $MAVEN_HOME/bin/mvn $* | sed -e "s/(\[INFO\]) Building( .*)/ ${info}\1${projectname}\2 ${c_end}/g" \
  -e "s/(Time elapsed: )([0-9]+[.]*[0-9]*.sec)/${c_cyan}\1${c_white}\2${c_end}/g" \
  -e "s/(Downloading: .*)/${downloading}\1${c_end}/g" \
  -e "s/BUILD FAILURE/${error}BUILD FAILURE${c_end}/g" \
  -e "s/WARNING: ([a-zA-Z0-9.-/\\ :]+)/${warn}WARNING: \1${c_end}/g" \
  -e "s/SEVERE: (.+)/${c_white}${c_bg_red}SEVERE: \1${c_end}/g" \
  -e "s/Caused by: (.+)/${c_white}${c_bg_green}Caused by: \1${c_end}/g" \
  -e "s/Running (.+)/${c_green}Running \1${c_end}/g" \
  -e "s/FAILURE (\[[0-9]+.[:0-9]+s\])/${error}FAILURE \1${c_end}/g" \
  -e "s/SUCCESS (\[[0-9]+.[:0-9]+s\])/${success}SUCCESS \1${c_end}/g" \
  -e "s/(\[INFO.*)/${info}\1${c_end}/g" \
  -e "s/INFO: (.+)/${c_white}INFO: \1${c_end}/g" \
     -e "s/(\[WARN.*)/${warn}\1${c_end}/g" \
     -e "s/(\[ERROR.*)/${error}\1${c_end}/g" \
     -e "s/(<<< FAILURE!)/${error}\1${c_end}/g" \
     -e "s/Tests run: ([0-9]*), Failures: ([0-9]*), Errors: ([0-9]*), Skipped: ([0-9]*)/${c_green}Tests run: \1 ${c_end}, Failures: ${warn}\2 ${c_end}, Errors: ${error}\3 ${c_end}, Skipped:  ${skipped}\4 ${c_end}/g"
}

alias mvn=color_maven

NOTE: This script is intended to be used with Green text on Black background. Adjust the colors as you want to come up with something more personal

Download script from Github

Click here to view Gist on Github

How to install/use

In your ".bashrc" or ".bash_profile" add the following line:

source ~/<path to script>/colorize-maven.sh

January 17, 2012

Mocking with JodaTime's DateTime and Google Guava's Supplier

 

Introduction

If you're a seasoned unit tester, you've learned to take note when you see any code working with time, concurrency, random, persistence and disc I/O.

The reason for this is that tests can be very brittle and sometimes down-right impossible to test properly. This post will show how to abstract out "time" by injecting a replacement for it in the consumer.  This post will be using Spring 3 as the Dependency Injection container, though Guice, other DI containers or constructor/setters on POJOs would work as well.  I will also ignore Locales since the focus is on the injection of the DateTime, not DateTime itself.

Existing code

You've been handed a piece of code to unit test (or you are creating one and this is your first stab at it).  Our first piece of code, only one class: (This class is a Spring 3.1 controller and the purpose is to return back the current time as a String)

@Controller
@RequestMapping(value = "/time")
@VisibleForTesting
class TimeController {

    @RequestMapping(value = "/current", method = RequestMethod.GET)
    @ResponseBody
    public String showCurrentTime() {
        // BAD!!! Can't test
        DateTime dateTime = new DateTime();
        return DateTimeFormat.forPattern("hh:mm").print(dateTime);
    }
}

Take note that the class does a "new DateTime()" in the class.  Here is the corresponding test class:

What happens when we run the test?  How about assuming we have a very slow machine. You could (and most likely will) end up with your comparison DateTime to be different than the returned DateTime. This is a problem!

First thing to do is to remove the dependency, but how are we going to do this?  If we make the DateTime a field on the class, we will still have the same problem.  Introduce Google Guava's Supplier interface.


Google Guava Supplier

The Supplier interface only has one method, "get()" which will return an instance of whatever the supplier is setup for.  An example, the supplier will return a user's first name if they have logged in, and a default one if they have not:

public class FirstNameSupplier implements Supplier<String> {

    private String value;
    private static final String DEFAULT_NAME = "GUEST";

    public FirstNameSupplier() {
        // Just believe that this goes and gets a User from somewhere
        String firstName = UserUtilities.getUser().getFirstName();
        // more Guava
        if(isNullOrEmpty(firstName)) {
            value = DEFAULT_NAME;
        } else {
            value = firstName;
        }
    }

    @Override
    public String get() {
        return value;
    }
}

To your implementing method, you don't care what the first name is, only that you get one.


Refactoring out DateTime

Let's move on. For a much more real example of using a Supplier (and the point of this post) let's implement a DateTime supplier to give us back the current DateTime. While we're at it, let's also create an interface so that we can create mock implementations for testing:

public interface DateTimeSupplier extends Supplier<DateTime> {
    DateTime get();
}

and an implementation:

public class DateTimeUTCSupplier implements DateTimeSupplier {
    @Override
    public DateTime get() {
        return new DateTime(DateTimeZone.UTC);
    }
}

Now we can take the DateTimeUTCSupplier and inject that into our code that needs the current DateTime as the DateTimeSupplier interface:

@Controller
@RequestMapping(value = "/time")
@VisibleForTesting
class TimeController {

    @Autowired
    @VisibleForTesting
    // Injected DateTimeSupplier
    DateTimeSupplier dateTime;

    @RequestMapping(value = "/current", method = RequestMethod.GET)
    @ResponseBody
    public String showCurrentTime() {
        return DateTimeFormat.forPattern("hh:mm").print(dateTime.get());
    }
}

In order to test this, we'll need to create a MockDateTimeSupplier and have a controller to pass in the specific instance we want to return:

public class MockDateTimeSupplier implements DateTimeSupplier {

    private final DateTime mockedDateTime;

    public MockDateTimeSupplier(DateTime mockedDateTime) {
        this.mockedDateTime = mockedDateTime;
    }

    @Override
    public DateTime get() {
        return mockedDateTime;
    }
}

Notice that the object being saved is passed in via the constructor. This will not get you the current date/time back, but will return back the specific instance you want

and finally our test that exercises (slightly) the TimeController we implemented above:

public class TimeControllerTest {

    private final int HOUR_OF_DAY = 12;
    private final int MINUTE_OF_DAY = 30;

    @Test
    public void testShowCurrentTime() throws Exception {
        TimeController controller = new TimeController();
        // Create the mock DateTimeSupplier with our known DateTime
        controller.dateTime = new MockDateTimeSupplier(new DateTime(2012, 1, 1, HOUR_OF_DAY, MINUTE_OF_DAY, 0, 0));

        // Call our method
        String dateTimeString = controller.showCurrentTime();

        // Using hamcrest for easier to read assertions and condition matchers
        assertThat(dateTimeString, is(String.format("%d:%d", HOUR_OF_DAY, MINUTE_OF_DAY)));
    }

}

Conclusion

This post has shown how to use Google Guava's Supplier interface to abstract out a DateTime object so you can better design your implementations with unit testing in mind! Suppliers are a great way to solve the "just give me something", mind you it's a known type of something.


Good luck!

December 15, 2011

How to setup several GitHub accounts on multiple machines with separate SSH keys

Problem

So you've got a personal GitHub account, one for one client, one for another and you want to keep them all separate. How do you setup your box to clone private repositories based on which account you're using? Here's the simple guide on how to do this

Assumptions

I have set this up on Linux (Ubuntu 11.10) and Windows, but only via the Git Bash and/or Cygwin. This post will not cover how to setup and where to store SSH keys for Windows or Mac, though Mac should be fairly similar to Linux.

Setup first SSH key

I'm not going to go into depth on how to setup SSH keys, you can read more about that here. However, I will show the steps I used to get there. For first-account: (please replace "first-account" with your GitHub account)
  1. Change to your home .ssh folder
    cd ~/.ssh
    
  2. Setup your RSA key
    ssh-keygen -t rsa -f ~/.ssh/first-account-rsa -C "first-account@example.com"
    
  3. After following the directions, you should get something that looks like:
    The key fingerprint is:
    00:ff:00:cc:13:00:bb:ll:aa:hh
    
    The key's randomart image is:
    +--[ RSA 2048]----+
    |     .+   +      |
    |       = o P .   |
    |        = * *    |
    |       o = +     |
    |      o S .      |
    |     o o =       |
    |      o . E      |
    |                 |
    |                 |
    +-----------------+
    
    
  4. Open your GitHub.com account setup page, click on the "SSH Public Keys" tab.
  5. Create a new SSH key and copy the contents of ~/.ssh/first-account-rsa.pub and place into the new text field. Name it something unique so you can remember which machine you're using for your account.

    If you plan on each of your accounts needing access, open each of those accounts and add your SSH key to each account. This isn't very common, so only do this if you need to.
  6. To test, type in:
    ssh -t git@github.com
    

    As long as you do not see "access denied", you're good. You might see "failed on channel 0", this is ok.

Adding a second account

Now you should have your first account working, let's setup and add the second account.

  1. Repeat process 1-5 and stop (using the next account)
  2. Create a file named "config" in your ~/.ssh folder
    echo '' > ~/.ssh/config
    
    –or–
    touch ~/.ssh/config
    
  3. Use your favorite editor to add the configuration information for the SSH client to determine which RSA key to use:
    # first-account GitHub account
    Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/first-account-rsa # Note, this is the private key matching the .pub
    
    # second-account
    Host github-second-account  # NOTE: The host is NOT github.com, more on this further down
    HostName github.com
    User git
    IdentityFile ~/.ssh/second-account-rsa # Private key for the second account
    
    # repeat for the third and so on
    
  4. Save the file and your configuration is done

Let's look at the config file in a bit more detail. The first account is the base github.com host name, and you typically see a git URL as git@github.com:user/project.git. With your first account, you will leave this the domain the same**.

NOTE: This doesn't stop you from modifying your ~/.ssh/config file and instead of using "github.com" as your default host, setting the value to "github-first-account".

Now, for your second account, you will need to modify the URL you are using to clone the repository. This is done by changing the domain from "github.com" to "github-second-account". For example: git@github-second-account:user/project.git

Summary

You should now have the setup to clone and modify code for multiple accounts on the same machine. Repeat this process for each machine you would like to make available to GitHub.

December 3, 2011

Spring 3.1 + Cloud Foundry + Local Development

This post will help you build a Spring 3.1 web application using MongoDB on Cloud Foundry.  In addition to pushing to Cloud Foundry, you will also be able to develop in your local environment with a MongoDB instance.

Goals
The goals for this blog posting will be to build the application locally, then publish to your local Cloud Foundry instance.  We will utilize the Cloud Foundry runtime, and the new Spring Profiles

Setup
  1. Create an account with Cloud Foundry (https://www.cloudfoundry.com/micro)
  2. Follow instructions to setup your own Micro Cloud
    1. I use VMWare's player
    2. Verify "vmc info" that the micro cloud console matches
  3. Download MongoDB (at least version 2.0)
  4. Install and be familiar with Maven 3 (http://maven.apache.org)
  5. Familiarize yourself with Spring 3.1, Spring Data and Spring MongoDB
  6. Clone or download the source (https://github.com/mike-ensor/first-cloud-app)
  7. Run the app locally with:
    mvn clean package cargo:run -DskipTests
  8. Go to http://localhost:8080/home

Profiles
New in Spring 3.1 are the environment profiles which allow a developer to activate groups of beans based on an environment parameter.  There are several "gotchas" that I've discovered, one being an undocumented ordering for beans using profiles.

Take a look at data-services.xml.  Notice how the MongoTemplate is defined before the .  This is against my intuition because the MongoTemplate takes a reference to the MongoFactory object, which is defined below the MongoTemplate definition.

The second "gotcha" came from when and where to set the parameter to enable Spring's profiles.  The documentation and blogs do not explicitly mention that the developer must specify the profile that is active.  It was implied by the documentation that "default" was active by default, but this is not true.  In order for the default profile to be active, I added it as a system property in my cargo settings.  (as long as it is a system environment property, feel free to set it anywhere or any way you'd like).  Take a look at the pom.xml file around line 40 for the local Maven property and then around line 253 for the environment variable to be set.

Local development vs. Cloud Development
One of the main goals I had for interacting with Cloud Foundry is that I wanted a local development environment to speed up and ease development and reduce complexity with debugging.  Notice that in data-services.xml there is a "cloud" profile and a "default".  The point of the "default" profile is to have beans that are constructed when on a local environment.  You can see that there are two definitions of MongoFactory, one using Spring Data MongoDB's XML namespace and one using CloudFoundry Runtime's namespace.  I am not going to cover why these work the way they do, so if you'd like information, refer to http://blog.springsource.org/2011/04/12/cloud-foundry-for-spring-developers/ and http://blog.springsource.org/2011/11/09/using-cloud-foundry-services-with-spring-applications-part-3-the-cloud-namespace/

Pushing to Cloud Foundry
Now that you have a local running instance of the webapp, you will notice that the artifact is called "first-cloud-app.war", which you can find in the "/target" folder.  This is a problem when pushing to the Cloud Foundry instance since the name cannot contain any non-alpha characters.  Cloud Foundry's vmc tool is built from the VCAP open source project that is responsible for the open source PaaS services.  Another PaaS service includes App Fog, which allows you to basically use the same commands, but replace "vmc" for "af".  Both services fall victim to the naming problem.

In order to get around the naming problem, I have created a Maven profile cloud that builds the WAR artifact as "mikeensor.war".  Please change this to match your application's name since you won't have the user/password to publish (or the DNS) to publish to my micro instance.  The name will need to fit into the URL pattern http://<applicationname>..cloudfoundry.me.

To publish to your local cloud foundry micro instance, goto the root folder and type the following.  (this is assuming your micro instance is running and there are no "red" errors.

mvn clean package -Pcloud
vmc push <application name> -path target/

(if you have already pushed before, you will need to type:
vmc update <application name> -path target/

Note: It is possible to use the Maven Plugin for Cloud Foundry, however, I have still not been able to get it to work without changing the name of the artifact.

Enabling and connecting to services
You must create a service(s) so that your application can bind to the data source.  The VCAP (vmc) application handles how the configuration works when loading your application into Cloud Foundry.  It does this via an environment variable that is consumed in the namespaced configuration element.

In my example, I created a MongoDB service by typing:
vmc create-service mongodb --name <what you want to call your instance>

I named mine "second" (because I had created a first) and you will see that in data-services.xml the cloud XML configuration refers to the name of the service.
 
Note that if you have multiple MongoDB instances, you will need to do some Spring configuration (@Qualifier) when you want to use different instances.  This is not covered by this blog posting.

Now you will need to "bind" the service to your application.  This is done by typing:
vmc bind-service <name above> <application name>


Testing it out
You should be able to goto http://..cloudfoundry.me/home (example: http://mikeensor.mikeensor.cloudfoundry.me/home)

Congratulations!  You should have not only successfully deployed to Cloud Foundry (micro instance), bound to a MongoDB instance, but should be able to run in your local environment too!  As I get time, I will try to add in more detailed features such as multiple types of storage and post other "gotchas" as I find them.

August 2, 2011

Maven + JavaScript Unit Test: Running in a Continuous Integration Environment

JavaScript Unit Testing Continued

So you're still interested in unit testing JavaScript (good). This post is an extension of my much more indepth first posting on how to unit test JavaScript using JS Test Driver. Please check it out here.

Recap Last Posting

In the last posting we successfully unit tested JavaScript using Maven and JsTest Driver. This allowes us to test JavaScript when on an environment that has a modern browser installed and can be run.

Problem with typical CI environments

So what happens when the test are passing on your local box, but you go to check in your code and the Continuous Integration (CI) server pukes on the new tests becasue there is no "screen" to run chrome or firefox? As of this posting, none of the top-tier browsers have a "headless" or an in-memory only browser window. There are alternatives to running JavaScript in a browser, such as rhino.js, env.js or HtmlUnit, however, these are just ports of browsers and the JavaScript and DOM representation are not 100% accurate which can lead to problems with your code when rendered in a client's browser.

Approach

What we need to do is to run JSTestDriver's browser in a Virtual X Framebuffer (Xvfb) which is possible on nearly all Linux based systems. The example below uses a Solaris version of Linux, however, Debian and RedHat linux distrubutions come with the simplified bash script to easily run an appliation in a virtual framebuffer. This solution was derived from one posted solution on the JS Test Driver wiki (http://code.google.com/p/js-test-driver/wiki/ContinuousBuild. The given example is also a full working example that is in use at my current client.

Here is the quick list of what we will accomplish. Note, several of these steps are discussed in depth in the previous post and are not covered in depth here.
  1. Create a profile to run Js Unit-Tests
  2. Copy JsTestDriver library to a known location for Maven to use
  3. Copy JavaScript main and test files to known locations
  4. Use ANT to start JsTestDriver and pipe the screen into xvfb

Here is a sample profile to use. You will need to adjust the properties at the top of the profile to match your system.
<profile>
    <!-- Runs JS Unit Test inside of Xvfb -->
    <id>ci-jstests</id>
    <properties>
        <firefox.location>/opt/swf/bin/firefox</firefox.location>
        <js-test-driver.version>1.3.2</js-test-driver.version>
        <xvfb.location>/opt/X11R6/xvfb-run</xvfb.location>
    </properties>
    <build>
        <plugins>
            <!-- Copy JS Test Driver JAR to target folder -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.1</version>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>generate-resources</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>com.google.jstestdriver</groupId>
                                    <artifactId>jstestdriver</artifactId>
                                    <version>${js-test-driver.version}</version>
                                    <type>jar</type>
                                    <overWrite>true</overWrite>
                                    <destFileName>jsTestDriver.jar</destFileName>
                                </artifactItem>
                            </artifactItems>
                            <outputDirectory>${project.build.directory}/jstestdriver</outputDirectory>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>true</overWriteSnapshots>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- Copy JavaScript files and Tests files  -->
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.4.3</version>
                <executions>
                    <execution>
                        <id>copy-main-files</id>
                        <phase>generate-test-resources</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/test-classes/main-js</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>src/main/webapp/scripts</directory>
                                    <filtering>false</filtering>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                    <execution>
                        <id>copy-test-files</id>
                        <phase>generate-test-resources</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/test-classes/test-js</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>src/test/webapp/scripts</directory>
                                    <filtering>false</filtering>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.6</version>
                <executions>
                    <execution>
                        <phase>test</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <target>
                                <mkdir dir="${project.build.directory}/js-test-results" />
                                <exec executable="${xvfb.location}">
                                    <arg line="java" />
                                    <arg line="-jar ${project.build.directory}/jstestdriver/jsTestDriver.jar" />
                                    <arg line="--port 4220" />
                                    <arg line="--testOutput ${project.build.directory}/js-test-results" />
                                    <arg line="--config ${project.build.directory}/test-classes/jsTestDriver.conf" />
                                    <arg line="--browser ${firefox.location}" />
                                    <arg line="--tests all" />
                                    <arg line="--verbose" />
                                </exec>
                            </target>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</profile>

Possible problems

Although I cannot predict or fix all problems, I can share the one major problem I ran into with Solaris and the script used to fix that. In Solaris (and could happen to other distros) the xvfb-run script was not available and several of the other libraries did not exist. I first had to download the latest X libraries and place them in their appropriate locations on the CI server. Next, I had to re-engineer the xvfb-run script. Here is a copy of my script (NOTE: This is the solution for my server and this may not work for you)

I created a script that contains:

/usr/openwin/bin/Xvfb :1 screen 0 1280x1024x8 pixdepths 8 24 fbdir /tmp/.X11-vbf &