(filtered by tag 'build')

Recently I have created 2 tests for two classes that looked very similar. Well, they were same apart for testing different subclasses of the same parent. Indeed, tested classes were also similar. I have to fix this (on both sides if possible).

I have found this problem when I wanted to introduce new method to interface and was adding test for it. So my first idea was to reuse test implementation for both classes by creating abstract test and two subclasses handling different initialization of tested classes (different class to instantiate and also different colaborators). Problem is that these two classes together with their tests are not located in the same module. Natural place for abstract test is to put it into module that contains interface, because it is basically testing contract of that interface. Then add concrete tests into modules that implement interface. However our build system does not allow me to do that because tests are not part of built artefact, thus dependent modules do not see them.

A workaround for this problem is to introduce a new module that will contain abstract test masked as production class, hence it will be visible from other modules. Of course, this new helper module will not be included in installer of product. All problems solved... Not so fast, theoretically this works perfectly, but it might not be practical if that new module would contain only 1 or 2 abstract tests. It is heavy weight solution for such amount of code. Our codebase contains tens of small modules separating different parts of product to abstract and concrete modules (which is good) and introduction of test-supporting modules might help this number to grow rapidly.

Fortunatelly I have found that I can solve this particular problem differently this time, because only occured due to badly factored production classes. If I create abstract superclass for them, I can easily remove duplication of tests and add new test classes testing only logic that is different.

Somehow I am getting different moral conclusion that I planned with this post - "Look for real source of problems before attempting to fix them", but that's only because my case was special. In general, it is important to "Make sure your build system allows you to depend on tests from different modules" (only tests should depend, not production code). This allows you to do better tests regarding removing duplication by factoring common code to helpers and introduction of abstract interface tests.

Note about testing of intefaces

I was not able to google good example of inteface tests (Apart from that you can read about them in JUnit Recipes by J. B. Rainsberger, but that will not help you to get up to speed without having the book at hand. Still I encourage you to read it later.). So I am going to write a few lines about the idea. Let's have interface Stack.
public interface Stack {
    void push(Object data);
    Object pop();
    boolean isEmpty();
}
This interface has defined contract. It can be specified as Javadoc or any other way, but how can you make sure that all implementations comply to it?

The best way to achieve compliance is to provide abstract test case that implementor extends and provides method for creation of tested instance. Then she can execute test and everybody knows if it is compliant or not in fraction of second.

public abstract class StackTest {
    protected abstract Stack createInstance();

    public void testShouldPopLastPushedItem() {
        Stack tested = createInstance();
        //... rest of test
    }

    public void testShouldThrowExceptionWhenPoppingFromEmpty() {
        Stack tested = createInstance();
        //... rest of test
    }
}
Having these tests for contract it is quite easy to verify our cool implementation of Stack is working:
public class PersistentAndsSecureStackTest extends StackTest  {
    // add some setUp and tearDown if needed
    protected Stack createInstance() {
        return new PersistentAndsSecureStack();
    }
}

Back to the point

This is only one of the reasons why my tests need to be able to depend on tests from other modules. It is simply strong tool for making sure product is well tested and all duplication is removed.

I am still not able to set up dependency of tests between different modules. Fortunatelly I did not need it again.