ModeShape

An open-source, federated content repository

Testing behaviors

I mentioned in my last post how learning Ruby has made me a better Java developer. In particular, learning RSpec opened my eyes to a new way of unit testing.

RSpec is a library for Ruby that is built around Behavior Driven Development (BDD). In BDD and with RSpec, you focus on specifying the behaviors of a class and write code (tests) that verify that behavior. Whether you do this before you write the class is up to you, but I’ve found that outlining the class’ behaviors before (or while) I write the class helps me figure out what exactly the implementation should do.

You may be thinking that BDD sounds awfully similar to Test Driven Development (TDD). In some ways they are similar: they both encourage writing tests first and for fully testing the code you write. However, TDD doesn’t really guide you into the kinds of tests you should be writing, and I think a lot of people struggle with what they should be testing. BDD attempts to give you this guidance by getting the words right so that you focus on what the behaviors are supposed to be.

Let’s look at an example of a class that represents a playlist. The first step will be to decide what the class should and should not do:

Playlist

  • should not allow a null name
  • should not allow a blank name
  • should always have a name
  • should allow the name to change
  • should maintain the order of the songs
  • should allow songs to be added
  • should allow songs to be removed
  • should allow songs to be reordered
  • should have a duration that is a summation of the durations of each song
  • should not allow a song to appear more than once

Really, these are just the requirements written as a list. With BDD and JUnit 4.4, we can capture each behavior specification as a single unit test method. Initially, we’ll just stub the methods, but later on we’ll implement the test methods to verify the class actually exhibits that behavior. And since JUnit 4.4 gives us the freedom to name our test methods anything we want, let’s take a play from the RSpec playbook and put these behavior specifications directly in our test method names. Pretty cool! Just start listing the expected behaviors, and the test methods simply fall out:

public class PlaylistTest {
 @Test public void shouldNotAllowANullName() {}
 @Test public void shouldNotAllowABlankName() {}
 @Test public void shouldAlwaysHaveAName() {}
 @Test public void shouldAllowTheNameToChange() {}
 @Test public void shouldMaintainTheOrderOfTheSongs() {}
 @Test public void shouldAllowSongsToBeAdded() {}
 @Test public void shouldAllowSongsToBeRemoved() {}
 @Test public void shouldAllowSongsToBeReordered() {}
 @Test public void shouldHaveADurationThatIsASummationOfTheDurationsOfEachSong() {}
 @Test public void shouldNotAllowASongToAppearMoreThanOnce() {}
}

By capturing the requirements/behaviors in the test class, we don’t need to document them elsewhere. We can even add JavaDoc if the name isn’t clear. And, with a little work, we could generate that list of requirements by processing (or sequencing!) our code, as long as we follow the convention that the method names form a camel-case but readable English description of the behavior. (In fact, the org.jboss.dna.common.text.Inflector has a method to “humanize” camel-case and underscore-delimited strings, making it a cinch to output human readable code.)

And our test class even compiles. Pretty cool, huh? Oh, and that last requirement that’s not very intuitive? We now have a specification (test method) that verifies the seemingly odd behavior, so if a developer later on changes this behavior, it’ll get caught. (Of course the developer might just blindly change the test, but that’s another problem, isn’t it?)

But back to our development process. At this point, we could implement the test methods using the non-existent Playlist class. It may not compile, but we could then use our IDE to help us create the Playlist class and the methods we actually want. Of course, if this is too weird, you can always stub out the class and then implement the test methods. Personally, I like to implement some of the test methods before going any further, and we’ll use Mockito to stub out a Song implementation.

public class PlaylistTest {
  private Playlist playlist;
  private String validName;
  private Song song1;
  private Song song2;

  @Before
  public void beforeEach() {
    validName = "Pool party songs";
    playlist = new Playlist();
    song1 = mock(Song.class);
    song2 = mock(Song.class);
  }

  @Test(expected = IllegalArgumentException.class)
  public void shouldNotAllowANullName() {
    playlist.setName(null);
  }

  @Test(expected = IllegalArgumentException.class)
  public void shouldNotAllowABlankName() {
    playlist.setName("   ");
  }

  @Test
  public void shouldAlwaysHaveAName() {
    assertThat(playlist.getName(), is("New Playlist"));
  }

  @Test
  public void shouldAllowTheNameToChange() {
    validName = "New valid playlist name";
    playlist.setName(validName);
    assertThat(playlist.getName(), is(validName));
  }

  ...

  @Test
  public void shouldHaveADurationThatIsASummationOfTheDurationsOfEachSong() {
    stub(song1).getDurationInSeconds().toReturn(134);
    stub(song2).getDurationInSeconds().toReturn(205);
    playlist.add(song1,song2);
    assertThat(playlist.getDurationInSeconds(), is(339));
    verify(song1, times(1)).getDurationInSeconds();
    verify(song2, times(1)).getDurationInSeconds();
  }

  @Test
  public void shouldNotAllowASongToAppearMoreThanOnce() {
    stub(song1).equals(song2).toReturn(true);
    stub(song2).equals(song1).toReturn(true);
    assertThat( playlist.add(song1), is(true));
    assertThat( playlist.add(song2), is(false));
  }
}

Now we can complete the Playlist class and round out more tests as we discover new requirements and behaviors. Rinse and repeat. And we’ve done it all with just a little convention and JUnit 4.4, meaning it works in our IDE and in our continuous integration system.

The real change that BDD brings is just thinking differently. So while there are some Java frameworks for BDD (e.g., JDave and JBehave), the real benefit comes from changing your testing behavior, not changing your tools.

I hope this long post has inspired you to rethink how you do testing and to give BDD a try. Let us know what you find!

Advertisements

Filed under: techniques, testing, tools

Java developers should learn Ruby

Okay, it doesn’t have to be Ruby. Pick some other language. Pick Erlang, or even JavaScript. It doesn’t matter really matter, as long as it’s different enough from Java that it actually requires some effort, and that you keep your mind open to new things.

Why bother? Because learning other languages will make you a better Java developer. Seriously.

Learning another language forces you to dive into a different community. You’ll find different ideas and different approaches to many of the same problems. They may not be better ideas and approaches, just different. Other communities often have a fresh perspective on similar problems. And sometimes it will even make you appreciate what we really do have in the Java community (like a huge number of really great libraries).

Learning another language can teach you new idioms. Some you may be able to use in Java, and others you won’t. Ruby blocks, for example, are Ruby’s form of closures and are widely used in most Ruby programs. They’re extremely useful for running predefined code that delegates (perhaps repeatedly) to the block for custom behavior. Here’s a simple example of iterating over an array and doing some custom behavior (printing the element):

animals = ['lion','tiger', 'bear']
animals.each {|animal| puts animal }

Unfortunately, Java doesn’t have closures. Not really. The closest thing in Java 6 is to pass an anonymous inner class in much the same way that listeners are often used in GUI applications. All we need is a predefined interface and a method on a class that accepts the implementation and that performs the iteration (like the “each” method in Ruby). So pretend that java.util.List has an “each” method that takes an implementation of an OnEach:

public interface OnEach<T> {
    void run(T obj);
}
public interface List<T> ... {
    void each( OnEach&lt;T&gt; action );
}

Then our example would look something like this:

List<String> animals = Arrays.asList( new String[]{"lion", "tiger", "bear"} );
animals.each( new OnEach<String>() {
  public void run( String animal ) {
    System.out.println(animal);
  }
});

Kinda gross, huh? But even though it’s not as easily done, it’s a pattern that you can use in your designs to allow custom behaviors without requiring subclasses. There are several closure proposals for Java 7, but none are as easy as in Ruby or JavaScript. By the way, Alex has the best resource for all things Java 7.

Learning another language also forces you to use different tools and processes. One example in Ruby is RSpec, which is a Behavior Driven Development framework focused on specifying and verifying behaviors. BDD is a rich topic that I’ll explore in another post.

Another example is autotest, a great little tool from ZenTest that takes continuous integration to a whole new level. It works on your local machine (rather than a remote continuous integration server), and it simply monitors your development environment’s file system for changes to source files and runs the unit tests corresponding to any changed file(s). If those tests pass, then it runs all of the tests. It’s simple, elegant, and allows you to focus on changing the code, yet still get feedback from your tests. It’s like JUnit or TestNG Eclipse plugins that automatically run your unit tests as you work on the code.

The bottom line is that the Java community doesn’t have the market cornered on good ideas. Java is great and will continue to be, but it does need to evolve. Java first appeared over 13 years ago, and some of us have been developing primarily in Java for most of that time. Go exploring, and I’ll bet you’ll become a better Java developer for it.

Have you changed how you develop Java after learning another language?

Filed under: techniques, tools

New sequencers

We’ve been making a lot of great progress on JBoss DNA lately. We’re getting a lot of interest, and several folks have stepped forward to contribute new sequencers and to work on various components. So in addition to the image sequencer that came with the 0.1 release, the codebase (in trunk) now has sequencers for:

  • MP3 audio files, contributed by Stefano Maestri
  • General XML files, contributed by John Verhaeg
  • Microsoft Office files, including general metadata for all documents and detailed information for Microsoft PowerPoint presentations and Microsoft Excel spreadsheets, contributed by Michael Trezzi
  • Java source files with annotations, contributed by Serge Pagop
  • JCR CND files, contributed by Dan Florian

Several of these are still in progress, but they’re coming along quickly. A couple of them are relying on some upcoming enhancements to the sequencing framework, so that’s got to be done first.

Stefano has also been working on another sequencer for JBoss ESB messages, which should be really cool.

We have lots of other ideas for sequencers. Do you have any suggestions? If so, let us know.

Filed under: features, jcr, repository

ModeShape is

a lightweight, fast, pluggable, open-source JCR repository that federates and unifies content from multiple systems, including files systems, databases, data grids, other repositories, etc.

Use the JCR API to access the information you already have, or use it like a conventional JCR system (just with more ways to persist your content).

ModeShape used to be 'JBoss DNA'. It's the same project, same community, same license, and same software.

ModeShape

Topics