Skip to content

Commit

Permalink
Migrate existing posts from Octopress
Browse files Browse the repository at this point in the history
  • Loading branch information
marcphilipp committed Sep 15, 2014
1 parent b032d02 commit 09e8fd0
Show file tree
Hide file tree
Showing 15 changed files with 1,149 additions and 28 deletions.
83 changes: 83 additions & 0 deletions _posts/2010-02-13-experimenting-with-theories.markdown
@@ -0,0 +1,83 @@
---
layout: post
title: "Experimenting with Theories"
date: 2010-02-13 14:04
comments: true
categories: [JUnit]
---

The very first 4.x release of JUnit contained support for custom test runners. Moreover, it came with the `Parameterized` test runner that allows to execute the test cases in a test class against a collection of values, i.e. parameters.

The example that comes with the Javadoc of the `Parameterized` class tests an imaginary Fibonacci calculator for a number of data points:

{% highlight java %}
@RunWith(Parameterized.class)
public class FibonacciParameterizedTest {

@Parameters
public static List<Object[]> data() {
return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 },
{ 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
}

private final int input;
private final int expected;

public FibonacciParameterizedTest(int input, int expected) {
this.input = input;
this.expected = expected;
}

@Test
public void test() {
assertEquals(expected, Fibonacci.compute(input));
}
}
{% endhighlight %}

JUnit 4.4 introduced Theories. A theory is an abstraction of a concrete test scenario, i.e. while a test specifies the behavior in one particular case, a theory captures more than a single scenario but is usually not as detailed in its assertions.

When using a Parameterized test you usually specify the input along with the expected output. Of course, you can do the same with a theory. However, theories allow for a complete different approach to testing the Fibonacci calculator.

E.g. a simple theory could state that for one of the seeds, i.e. 0 or 1, the same number is returned as result. Another theory could test the recurrence relation, i.e. that `Fibonacci(n)` always equals `Fibonacci(n-1)` + `Fibonacci(n-2)`. In Java this can be written as:

{% highlight java %}
@RunWith(Theories.class)
public class FibonacciTheories {

@DataPoints
public static int[] VALUES = { 0, 1, 2, 3, 4, 5, 6 };

@Theory
public void seeds(int n) {
assumeTrue(n <= 1);
assertEquals(n, compute(n));
}

@Theory
public void recurrence(int n) {
assumeTrue(n > 1);
assertEquals(compute(n - 1) + compute(n - 2), compute(n));
}
}
{% endhighlight %}

Even shorter yet:

{% highlight java %}
@RunWith(Theories.class)
public class FibonacciTheories {

@Theory
public void seeds(@TestedOn(ints = { 0, 1 }) int n) {
assertEquals(n, compute(n));
}

@Theory
public void recurrence(@TestedOn(ints = { 2, 3, 4, 5, 6 }) int n) {
assertEquals(compute(n - 1) + compute(n - 2), compute(n));
}
}
{% endhighlight %}

So, which test is better? Actually, I am still undecided. While theories certainly look more elegant, a parameterized tests states its assumptions more clearly. The answer seems to be, as always: It depends.
70 changes: 70 additions & 0 deletions _posts/2010-02-16-generic-matcher-pitfalls.markdown
@@ -0,0 +1,70 @@
---
layout: post
title: "Generic Matcher Pitfalls"
date: 2010-02-16 22:45
comments: true
categories: [JUnit, Hamcrest]
---

Using [Hamcrest](http://code.google.com/p/hamcrest/) matchers in combination with `assertThat` allows for more fluid specification of JUnit assertions.

Recently, while working on the backend of [Project Usus](http://projectusus.org/), we needed a simple matcher, that would test whether a given set is empty. At the time, we reused a set matcher we had already written a few minutes earlier.

Today, I had another look at the pre-defined matchers that come with Hamcrest and found the `empty()` matcher in `org.hamcrest.Matchers`. Since I'm not concerned with the actual implementation (at least for now), I'll just give you the factory method:

{% highlight java %}
@Factory
public static <E> Matcher<Collection<E>> empty() {
return new IsEmptyCollection<E>();
}
{% endhighlight %}

Great, I thought. So I readily changed our tests to use the pre-defined matcher…

{% highlight java %}
assertThat(new TreeSet<String>(), empty());
{% endhighlight %}

However, this yielded a compile error because the compiler could not infer the type parameter of the method. It *did* work when stating the type parameter of the static method explicitly:

{% highlight java %}
assertThat(new TreeSet<String>(), Matchers.<String>empty());
{% endhighlight %}

But that looked horrible. My first shot was to define an own factory method…

{% highlight java %}
@Factory
public static <E> Matcher<Collection<E>> emptyOf(Class<E> clazz) {
return new IsEmptyCollection<E>();
}
{% endhighlight %}

…that can be used like this:

{% highlight java %}
assertThat(new TreeSet<String>(), emptyOf(String.class));
{% endhighlight %}

I was still not very pleased with the solution. Even more since it does not matter at all what kind of objects are inside the collection to determine whether it is empty. After playing around for a little while I came up with this solution:

{% highlight java %}
public class IsEmptyCollection extends TypeSafeMatcher<Collection<?>> {

@Override
protected boolean matchesSafely(Collection<?> collection) {
return collection.isEmpty();
}

public void describeTo(Description description) {
description.appendText("empty");
}

@Factory
public static Matcher<Collection<?>> empty() {
return new IsEmptyCollection();
}
}
{% endhighlight %}

In conclusion, I think it is not trivial to write usable generic matchers. Therefore, avoid generics when you don't need them!
76 changes: 76 additions & 0 deletions _posts/2010-03-13-applying-dry-to-junit-categories.markdown
@@ -0,0 +1,76 @@
---
layout: post
title: "Applying DRY to JUnit Categories"
date: 2010-03-13 22:08
comments: true
categories: [JUnit]
---

Long awaited, [JUnit 4.8](http://kentbeck.github.com/junit/doc/ReleaseNotes4.8.html) introduced support for categorizing test cases.

A category marker is simply a class or interface, e.g.

{% highlight java %}
public interface SlowTests {}
{% endhighlight %}

Tests can be marked using the `@Category` annotation:

{% highlight java %}
public class A {
@Test
public void a() {}

@Category(SlowTests.class)
@Test
public void b() {}
}
{% endhighlight %}

The annotation works both on methods and classes:

{% highlight java %}
@Category(SlowTests.class)
public class B {
@Test
public void c() {}
}
{% endhighlight %}

Test suites that include or exclude the `SlowTests` category are defined by specifying the `Categories` runner and using the `@ExcludeCategory` or `@IncludeCategory` annotation, respectively:

{% highlight java %}
@RunWith(Categories.class)
@SuiteClasses( { A.class, B.class })
@ExcludeCategory(SlowTests.class)
public class AllFastTests extends AllTests {}

@RunWith(Categories.class)
@SuiteClasses( { A.class, B.class })
@IncludeCategory(SlowTests.class)
public class AllSlowTests extends AllTests {}
{% endhighlight %}

In this example, `AllFastTests` would execute only `A.a` while `AllSlowTests` would ignore `A.a` but run `A.b` and `B.c`.

However, there is a major issue in the above suite declarations: they violate the [DRY](http://c2.com/cgi/wiki?DontRepeatYourself "Don't Repeat Yourself") principle. Both test suites list all test classes in the `@SuiteClasses` annotation. While it seems feasible to maintain the list of test classes at two locations for a small number of classes, it certainly is not a viable option in a real-world setting, especially when there are multiple categories.

Fortunately, there is a simple solution: use inheritance. You can define the list of test classes once in a normal test suite …

{% highlight java %}
@RunWith(Suite.class)
@SuiteClasses( { A.class, B.class })
public class AllTests {}
{% endhighlight %}

… and declare subclasses that filter the list of classes by category:

{% highlight java %}
@RunWith(Categories.class)
@ExcludeCategory(SlowTests.class)
public class AllFastTests extends AllTests {}

@RunWith(Categories.class)
@IncludeCategory(SlowTests.class)
public class AllSlowTests extends AllTests {}
{% endhighlight %}
101 changes: 101 additions & 0 deletions _posts/2010-05-13-combining-suitebuilder-and-classpathsuite.markdown
@@ -0,0 +1,101 @@
---
layout: post
title: "Combining SuiteBuilder and ClasspathSuite"
date: 2010-05-13 13:05
comments: true
categories: [JUnit]
---

In a recent [commit](http://github.com/KentBeck/junit/commit/f09cff79b941a525271f3f2838a9742b4c5c8d36) to JUnit Kent Beck and David Saff have added an "alpha-ready implementation of `SuiteBuilder`". As Kent Beck previously described in a [blog post](http://www.threeriversinstitute.org/blog/?p=456), the idea behind the `SuiteBuilder` runner is to use annotations on fields instead of annotations on classes.

### Limitations of regular test suites

While an annotation can take parameters the arguments must be literals, e.g. constant String values or class literals. For example, the classic `Suite` runner is configured using the `@SuiteClasses` annotation that takes an array of class literals, i.e. the test classes to be run:

{% highlight java %}
@RunWith(Suite.class)
@SuiteClasses({
SomeTest.class,
YetAnotherTest.class
})
public class AllTests {}
{% endhighlight %}

Literals have a severe limitation: they must be know at compile-time! Thus, when using the `Suite` runner, there was no way of determining the classes to run by any other means such as scanning the current classpath.

### ClasspathSuite to the rescue

For this original purpose, Johannes Link created the [`ClasspathSuite`](http://johanneslink.net/projects/cpsuite.jsp) runner. Its basic usage is very simple: just specify it using the `@RunWith` annotation. In addition, you can also include test classes in JAR files, filter by class names or types, and so on:

{% highlight java %}
@RunWith(ClasspathSuite.class)
@IncludeJars(true)
@ClassnameFilters({".*Test", "!.*AllTests"})
@BaseTypeFilter(MyBaseTest.class)
public class AllTests {}
{% endhighlight %}

However, the `ClasspathSuite` does not support JUnit's categories as mentioned in an [earlier blog post]({{ root_url }}/blog/2010/03/13/applying-dry-to-junit-categories/). While it could certainly be extended to support the Category-related annotations `@IncludeCategory` and `@ExcludeCategory`, the `SuiteBuilder` offers a more flexible alternative.

### Introducing SuiteBuilder

The `SuiteBuilder` runner is similar to the `Suite` runner, but reads the test classes it is supposed to run from a field of the suite class annotated with `@Classes`. The field can be freely initialized to hold an implementation of the `SuiteBuilder.Classes.Value` interface which simply wraps a collection of classes. E.g., the first example can be rewritten using the `SuiteBuilder`:

{% highlight java %}
@RunWith(SuiteBuilder.class)
public class AllTests {
@Classes
public Listed classes =
new Listed(SomeTest.class, YetAnotherTest.class);
}
{% endhighlight %}

In addition, you can filter the resulting test runners by annotating a field of type `SuiteBuilder.RunnerFilter.Value` with `@RunnerFilter`. For example, the latest commit included a `CategoryFilter` that filters tests by category:

{% highlight java %}
@RunWith(SuiteBuilder.class)
public class OnlyYes {
@Classes
public Listed classes =
new Listed(SomeTest.class, YetAnotherTest.class);

@RunnerFilter
public CategoryFilter filter = CategoryFilter.include(Yes.class);
}
{% endhighlight %}

### Putting the pieces together

So what? Well, instead of specifying the classes explicitly you could employ the capabilities of the `ClasspathSuite` to determine the test classes dynamically. For this purpose, I have written a small wrapper around Johannes Links' `ClasspathSuite`. The above example can thus be rewritten without explicitly specifying the test classes:

{% highlight java %}
@RunWith(SuiteBuilder.class)
public class OnlyYes {
@Classes
public InClasspath classes = new InClasspath();

@RunnerFilter
public CategoryFilter filter = CategoryFilter.include(Yes.class);
}
{% endhighlight %}

The wrapper offers the same flexibility as the `ClasspathSuite`, e.g.:

{% highlight java %}
@RunWith(SuiteBuilder.class)
public class OnlyYes {
@Classes
public InClasspath classes = new InClasspath().includingJars()
.filteredBy(".*Test", "!.*AllTests")
.includingOnlySubclassesOf(MyBaseTest.class);

@RunnerFilter
public CategoryFilter filter = CategoryFilter.include(Yes.class);
}
{% endhighlight %}

While I will look into how this can be integrated into JUnit or ClasspathSuite feel free to contact me if you are interested in the source code of the `InClasspath` class.

### Update

I am currently working on integrating ClasspathSuite and InClasspath into core JUnit... In the meantime, you can [take a look at the code on GitHub](http://github.com/marcphilipp/junit/tree/master/src/main/java/org/junit/experimental/cpsuite/).

0 comments on commit 09e8fd0

Please sign in to comment.