Showing posts with label Hamcrest. Show all posts
Showing posts with label Hamcrest. Show all posts

17 July 2015

Using Hamcrest Matchers With Composer

gotta matchTo help my friends of the Vienna PHP User Group to get started with Coding Dojos, I created a project template. Obviously the used programming language was PHP and PHPUnit, the PHP xUnit implementation, was required.

Composer
Setting up a new project almost always starts with dependency management and I used Composer to define my dependencies. Composer was configured by its composer.json,
{
  "name": "codecop/CodingDojo-PHP",
  "description": "Coding Dojo template",
  "license": "BSD",
  "require": {
  },
  "require-dev": {
    "phpunit/phpunit": "4.5.*"
  }
}
After verifying my composer installation with composer --version I downloaded PHPUnit with composer install. PHPUnit and its transitive dependencies were installed into the vendor directory as expected.

PHPUnit
Next I configured PHPUnit with its phpunit.xml,
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.5/phpunit.xsd"
    backupGlobals="false"
    colors="true"
    beStrictAboutTestsThatDoNotTestAnything="true"
    beStrictAboutOutputDuringTests="true"
    beStrictAboutTestSize="true"
    beStrictAboutTodoAnnotatedTests="true"
    verbose="true">

    <testsuites>
        <testsuite name="All Tests">
            <directory suffix="Test.php">test</directory>
        </testsuite>
    </testsuites>
</phpunit>
This told PHPUnit to load all the *Test.php files inside the test directory for test classes. I especially like the beStrictAbout* flags and enabled them all. These flags warn about smelly tests, e.g. test methods without any assertions. I ran PHPUnit with ./vendor/bin/phpunit to verify my setup. It did not show any tests - after all this was a new and empty project. I have seen people creating an alias to run PHPUnit, but I created a (Windows) script phpunit.bat in the local directory with the same effect,
@call "%~dp0vendor\bin\phpunit" %*
Now I was ready to go and wrote some unit tests, e.g.
<?php

require 'Hello.php';

class HelloTest extends \PHPUnit_Framework_TestCase {

    /** @test */
    function shouldReturnHelloName() {
        $greeter = new Greeter();
        $this->assertEquals("Hello Peter", $greeter->greet("Peter"));
    }

}
Hamcrest Matchers
In the Java community Hamcrest Matchers are popular and they even ship with the core JUnit framework. I like Hamcrest because it allows me to write my own matchers, which make assertions much more expressive than plain assertEquals. Luckily there were some ports of it and I was happy to see a Hamcrest PHP port. I added it to composer.json,
"require-dev": {
  "phpunit/phpunit": "4.5.*",
  "hamcrest/hamcrest-php": "1.2.*"
}
and updated my installation with composer install. Hamcrest offers global functions for its matchers, which allow for shorter syntax, especially when matchers are chained together. To enable this global functions, Composer has to auto load the main Hamcrest file, which is configured using autoload-dev in composer.json,
"autoload-dev": {
  "files": ["vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest.php"]
}
Using global functions has some drawbacks and is considered a bad practise in large scale projects. There are different ways to use Hamcrest PHP with Composer without loading the global functions, e.g. see Hamcrest PHP issues at GitHub. For a first time Coding Dojo I wanted to stay with the simplest way to use Hamcrest and kept the global functions.

So I was able to write my unit tests using Hamcrest matchers, e.g.
<?php

require 'Hello.php';

class HelloTest extends \PHPUnit_Framework_TestCase {

    /** @test */
    function shouldReturnHelloName() {
        $greeter = new Greeter();
        assertThat($greeter->greet("Peter"), equalTo("Hello Peter"));
    }

}
While the test above succeeded, PHPUnit refused to show me a green bar. This was because Hamcrest's assertThat was not recognised as assertion and PHPUnit marked the test as erroneous. With a heavy heart I had to remove one of PHPUnit's beStrictAbout flags,
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.5/phpunit.xsd"
    backupGlobals="false"
    colors="true"
    beStrictAboutTestsThatDoNotTestAnything="false"
    beStrictAboutOutputDuringTests="true"
    beStrictAboutTestSize="true"
    beStrictAboutTodoAnnotatedTests="true"
    verbose="true">
That was it. Finally I was ready to rock, matchers included!

(The complete project as zip is here.)

21 February 2014

Assert or MatcherAssert

#9 differenceI regularly meet online with friends and practice remote pair programming. Last time, while working with my friend Thomas, one of us mistyped the static import for assertThat and we ended with an import of org.hamcrest.MatcherAssert instead of org.junit.Assert. I had never seen MatcherAssert before and Thomas asked me if I knew what the actual difference between them would be. I did not know but we are going to find out right now.

A little bit of history
The Hamcrest Matchers are part of JUnit 4 since version 4.4. JUnit up to version 4.10 shipped with Hamcrest 1.1 and on release 4.11 it switched to Hamcrest 1.3. Looking at the history of MatcherAssert it seems that it had been in the core already but had been moved out again before version 1.1 was released.
  • May 22, 2007 - Moved assertThat() into hamcrest-core.
  • May 23, 2007 - Moved assertThat() back into hamcrest-integration (really this time).
  • July 19, 2007 - JUnit 4.4 shipped including Hamcrest 1.1
  • November 25, 2008 - Moved MatcherAssert to core.
  • July 9, 2012 - Hamcrest version 1.3 released
  • November 14, 2012 - shipped JUnit 4.11
Show me the code!
Now let us compare the actual source code. JUnit's Assert.assertThat looks like
public static <T> void assertThat(T actual, Matcher<T> matcher) {
   assertThat("", actual, matcher);
}

public static <T> void assertThat(String reason, T actual,
                                    Matcher<T> matcher) {
   if (!matcher.matches(actual)) {
      Description description = new StringDescription();
      description.appendText(reason);
      description.appendText("\nExpected: ");
      description.appendDescriptionOf(matcher);
      description.appendText("\n     got: ");
      description.appendValue(actual);
      description.appendText("\n");

      throw new AssertionError(description.toString());
   }
}
whereas Hamcrest's MatcherAssert code is
public static <T> void assertThat(T actual, Matcher<? super T> matcher) {
   assertThat("", actual, matcher);
}

public static <T> void assertThat(String reason, T actual,
                                    Matcher<? super T> matcher) {
   if (!matcher.matches(actual)) {
      Description description = new StringDescription();
      description.appendText(reason)
                 .appendText("\nExpected: ")
                 .appendDescriptionOf(matcher)
                 .appendText("\n     but: ");
      matcher.describeMismatch(actual, description);

      throw new AssertionError(description.toString());
   }
}

public static void assertThat(String reason, boolean assertion) {
   if (!assertion) {
      throw new AssertionError(reason);
   }
}
assertThat(String, boolean)
The most obvious difference is that Hamcrest defines an additional assertThat method which accepts a boolean expression. The method is identical to Assert.assertTrue(String, boolean). No big deal.

<? super T>
The signatures of both classes are almost identical. Almost because Hamcrest has a variation of the generic type T. JUnit accepts matchers that have the same type as the actual value in the argument list T actual, Matcher<T> matcher, while Hamcrest also accepts them for super types of T in T actual, Matcher<? super T> matcher. Allowing parent types of T makes sense because what matches the parents of T will always match T too. Here is an example of a Matcher<Number> matching against an Integer which extends Number.
import org.hamcrest.CustomMatcher;
...

@Test
public void shouldAllowSuperTypeMatcherInAssert() {
   Integer actual = 3;
   Matcher<Number> matcher = new CustomMatcher<Number>("anything") {
      @Override
      public boolean matches(Object anything) {
         return true;
      }
   };
   MatcherAssert.assertThat(actual, matcher); // (1)
   Assert.assertThat(actual, matcher); // (2)
}
Line (1) binds <T> to <Integer> because <? super Integer> allows Number as type of the given matcher. On the other hand, line (2) compiles as well, setting <T> directly to <Number>. So the two signatures are equivalent.

Description of the Mismatch
The third difference is the way the two assertions describe the mismatch. While JUnit just appends the actual value with description.appendValue(actual), Hamcrest asks the matcher to describe its mismatch by matcher.describeMismatch(actual, description), which might give more information in case of failure. So let us dig into some more code.
@Test
public void shouldDisplaySimilarMessageForIsMatcher() {
   int actual = 1;
   Matcher<Integer> matcher = is(2);
   assertJUnitMessage(actual, matcher,
      "Message\nExpected: is <2>\n  got: <1>\n");
   assertHamcrMessage(actual, matcher,
      "Message\nExpected: is <2>\n  but: was <1>");
}
As the BaseMatcher's describeMismatch just returns "was " + actual there is no real difference in the output of both assertions. What about other matchers? Let us look for implementations of describeMismatch that do more than just return the actual value. The only matcher I found was org.hamcrest.TypeSafeMatcher, which has several subclasses in the Hamcrest library, e.g. HasProperty, IsEmptyIterable and IsCloseTo.
import org.hamcrest.beans.HasProperty;
import org.hamcrest.collection.IsEmptyIterable;
import org.hamcrest.number.IsCloseTo;
...

@Test
public void shouldDisplaySimilarMessageForHasPropertyMatcher() {
   Object actual = "abc";
   Matcher<Object> matcher = HasProperty.hasProperty("prop");

   assertJUnitMessage(actual, matcher,
      "Message\nExpected: hasProperty(\"prop\")\n  got: \"abc\"\n");
   assertHamcrMessage(actual, matcher,
      "Message\nExpected: hasProperty(\"prop\")\n  but: no \"prop\" in \"abc\"");
}

@Test
public void shouldDisplaySimilarMessageForIsEmptyIterableMatcher() {
   Iterable<String> actual = Collections.<String> singletonList("a");
   Matcher<Iterable<String>> matcher = IsEmptyIterable.<String> emptyIterable();

   assertJUnitMessage(actual, matcher,
      "Message\nExpected: an empty iterable\n  got: <[a]>\n");
   assertHamcrMessage(actual, matcher,
      "Message\nExpected: an empty iterable\n  but: [\"a\"]");
}

@Test
public void shouldDisplaySimilarMessageForIsCloseToMatcher() {
   double actual = 2.0;
   Matcher<Double> matcher = IsCloseTo.closeTo(1, 0.1);

   assertJUnitMessage(actual, matcher,
      "Message\nExpected: a numeric value within <0.1> of <1.0>\n  got: <2.0>\n");
   assertHamcrMessage(actual, matcher,
      "Message\nExpected: a numeric value within <0.1> of <1.0>\n  but: <2.0> differed by <0.9>");
}
Hamcrest creates slightly more detailed error messages, but only for these three cases.

The Same DifferenceConclusion
org.hamcrest.MatcherAssert is not a replacement for org.junit.Assert because it does not come with all the assertions which we are used to. But MatcherAssert contains a slightly different assertThat. Using that method could potentially give better error messages because the matcher is called to describe the mismatch. When using a complex, custom matcher this could improve the error messages and speed up error diagnosis. Currently only three matchers implement their own descriptions. These come with the Hamcrest library which is not included with JUnit right now.