Monday, February 22, 2016

Self-Testing Java Classes

Way back in the COM days when everyone was talking about components and component marketplaces, I first heard of the idea of building tests directly into the production code. That potentially brings together a bunch of different software engineering disciplines that are near and dear to my heart -- testing, reusability, and design.

Last weekend, I finally got time to start playing with an idea I've been kicking around for a long time -- self-testing Java classes. My approach -- still in what I consider the "prototype" stage -- is based on a series of annotations on methods. With Java 8, we can finally stick multiple annotations of the same type on annotation targets. This is fortunate, because you'll see that this is kind of a prerequisite of my implementation. To explain, it's probably best to start from the outside in.

Here are a few simple tests for an absolute-value function:

  @Comparison(inputs = {"50"}, output="50")
  @Comparison(inputs = {"-40"}, output="40", description="Abs of a negative")
  @Comparison(inputs = {"-40"}, output="0", type=Type.GREATER_THAN_OR_EQUALS)
  public int abs(int input) {
    return input >= 0 ? input : -input;
  }

Each @Comparison annotation translates to a unit test case where the given inputs are passed to the annotated method and the specified output is expected. There is an optional comparison type, which defaults to EQUALS.

The next question deals with how these tests are executed. I wrote a custom JUnit Runner that:
  1. Looks for methods in the test class marked with @SelfTestMethod.
  2. Invokes self-testing on the object(s) those methods return.
  3. Reports the results in typical Unit style.
Thus, an entire JUnit test suite can look like this:

@RunWith(SelfTestRunner.class)
public class MathOperationsTest {
  @SelfTestMethod(name = "Default math operations")
  public MathOperations selfTestMathOperations() {
    return new MathOperations();
  }
}

After a run in Eclipse:

 

 Let's talk about where this potentially works well, and where it doesn't. If you have side-effect-free methods (in the style advocated by functional-programming gurus) that don't require a lot of test setup, you might be able to effectively test them this way.

On the other hand, if you need to set up a lot of state before executing the test, or your class requires one or more collaborators that need to be mocked/stubbed/faked, this probably won't be the way to go. And currently there is no support for method inputs that are not Java Strings or primitives (although this is high on my list to address).

That said, I plan to continue extending this work and seeing how many different scenarios I can push it into. Naturally, ideas are welcome, so feel free to reach out here or on Github.

Sunday, February 7, 2016

Implicit Class for List Element Extraction in Scala

Last weekend at the library, I stumbled upon the book "Realm of Racket", and it occurred to me that I should see if I can get my daughter to learn a bit about Lisp-like languages before she dives into Java in high school next year. Of course, I also had to start working through the Racket book myself. Unsurprisingly, it's fun.

One little throwaway thing I learned was that Racket has named list extract methods for first, second, third, up to tenth. Perhaps that's not super-useful, but I wanted it in my Scala toolbox nonetheless.

So I implemented them in a Scala implicit class and put them on Github as well as here for your enjoyment:

/**
 * Implicit methods that add "first" through "tenth"
 * extraction methods to the Scala List.
 */
object ListMethods {
  implicit class Nth[T](lst: List[T]) {
    def first = lst(0)
    def second = lst(1)
    def third = lst(2)
    def fourth = lst(3)
    def fifth = lst(4)
    def sixth = lst(5)
    def seventh = lst(6)
    def eighth = lst(7)
    def ninth = lst(8)
    def tenth = lst(9)
  }
}

It's just about the simplest possible implicit class implementation I can think of -- ten repetitive one-liners. Nonetheless, if you're having trouble structuring an implicit class, perhaps this helps you.

Also, I took the opportunity to try something new -- FunSuite from ScalaTest -- for unit testing. (Keep in mind that ScalaTest actually supports a few different unit testing styles. Here is a snippet of the test code:


class ListMethodsTest extends FunSuite {
  val lst = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

  test("first method works") {
    assert(1 == lst.first)
  }

  // etc