Ensuring test execution

Writing tests is important, but even if you do it before you run code, it is not enough to make the most use of their valuable feedback. You have to execute the tests, too.

Imagine this. You work test-driven. You start with a unit test. Then you write an integration test. Implement the required code. Run the tests. Everything is fine. You commit both the test and the code.

From this point on, you are likely to not run those particular tests unless they become red on the integration server. But, for them to be red on the server, they have to be executed by that server. How can you ensure that this happens flawlessly?

Let's look at the problem in more details. Suppose you are developing in Java. If so, these tests are typically run based on some naming convention. For example, an easy way to ensure that from the command line that all tests are run in a Java system, you can rely on the convention saying that the name of the class follows a Test pattern and that the tests are placed in a folder named tests.

This is all fine, but how do you ensure that you are naming all your test classes like this? This is particularly problematic given that the test runner, like the one in Eclipse, likely relies on the actual @Test annotations to run the tests regardless of class names.

Using Moose, you can build a rule:

^ self model allModelClasses select: [ :each |
   each isJUnit4TestCase and: [ 
      each isInMyTestPath and: [
         ('*Test*' match: each mooseName) not ] ] ]

This rule reveals all JUnit classes that fulfill the isInMyTestPath convention but not the Test one. Integrating this rule in the daily assessment routine can ensure that your valuable tests will be run by the server.

Are we ready? Not just yet.

You still have to ensure that your JUnit classes are all placed in the tests folder. Moose to the rescue:

^ self model allModelClasses select: [ :each |
   each isJUnit4TestCase and: [
      each isInMyTestPath not ]

This rule identifies all misplaced JUnit classes.

But why have two rules? Indeed, you could write a rule that integrates the two:

^ self model allModelClasses select: [ :each |
   each isJUnit4TestCase and: [
      each isInMyTestPath not or: [
         ('*Test*' match: each mooseName) not ] ] ]

In our case, it is actually better to have two rules because they exhibit two distinct problems. The first rule could be named MisplacedTestClasses to suggest to move your test to the proper location, while the second one could be named TestClassesWithWrongName thus documenting that the resolution is to rename the offending test class.

You could likely implement the above rules using regular expressions, instead of having a detailed model about your system like Moose provides. It would be less elegant, but simply checking for @Test in the file contents would do the trick. However, this is a technicality. The key is to understand that even if you might perceive the functionality of your system as your most valuable asset, you cannot discard the importance of your system's structure.

Now you are ready.

Posted by Tudor Girba at 13 July 2013, 11:03 pm with tags story, moose, assessment, analysis link
|