Showing posts with label General. Show all posts
Showing posts with label General. Show all posts

Saturday, May 21, 2011

Reuse and composition of Unit Tests

From time to time I want to be able to reuse already finished unit test. e.g. when I do a new implementation of an interface. In most cases, I did write an abstract base fixture, that I extend as needed:

public abstract class IterableFixture<T> {
    protected  abstract Iterable<T> create(); 
    @Test
    public void iterationDoesSomething(){
        assertThat(…);
    }
}

Whenever I need to write an Iterable<T> now, I can reuse the generic test like this:

public class MyIterableTest extends IterableFixture<Integer> {
    @Override
   
protected Iterable<Integer> create(){
        return new MyIterable<Integer>(…);

    } 
    @Test
    public void otherTest(){
        assertThat(…);
    }
}

Over the time, multiple base fixtures emerged, e.g. for equals/hashcode implementations, iteration, collections, serializing  and so on. When new classes needed multiple of these tests, I was forced to implement multiple test classes, e.g. MyFooIsSerializableTest, MyFooImplementsIterableTest, MyFooEqualsTest,… Although this was simple and practical, I never really  liked that I have too check for multiple green bubbles inside my IDE’s test runner for one class under test. I also did not like that I could not see in one spot what features my classes are implementing. This was extremely true for collection implementations, as they can be un/modifiable, im/mutable and fixed/variable-size. Not to speak about allowing null or similar variants.

What I really wanted, was to run my single test for my class and have each feature nicely listed, in a similar manner as JUnit Suites do:

test-runner-features

Just using a Suite did not help much, because I still had to extend my base fixtures. Additionally I had to add them to the Suites parameter list.

One of the reason that it was so clumsy to work with this approach are the plethora of derived classes - one for each feature that was implemented. So I decided, that using A factory that gets injected into the test classes constructor will simplify the approach. Then I looked at JUnits Parameterized runner, as it has the similar task to instantiate test fixture with constructor argument. The JUnit extension mechanism for custom runners showed to be very straight forward, so I decided to build my own Features Runner.

While thinking about how the using this runner I tried different variant on where to place which annotation. My requirements were:

  • Information shall  be defined statically using annotations
  • The user shall only have to implement the factories that create the systems under test
  • The factories shall be as  simple as possible
  • The factory implementations shall not impose any requirements on its constructors. This implies, that the user will new the factories, which give him the advantage that he can debug problems on more complex factories.
  • The feature shall look and feel like using JUnits built in runners. Parameterized is considered a nice template
  • Each generic feature fixture will have its on associated factory
  • I have spiked several versions. Here is an example of the final one. A class testing a collection for the features of being iterable and unmodifiable.

    @RunWith(Features.class)
    public class ArrayAdapterTestSuite {
        @Feature(Unmodifiable.class)
        public static Unmodifiable.Factory<Integer> unmodifiableFeature() {
            return new Unmodifiable.Factory<Integer>() {
                @Override
                public Collection<Integer> createCollection() {
                    return asList(0, 1, 2);
                }

                @Override
                public Integer createUniqueItem(int id) {
                    return id;
                }
            };
        }
        @Feature(Iterable.class)
        public static Iterable.Factory<Integer> iterableFeature() {
            return new Iterable.Factory<Integer>() {
                @Override
                public java.lang.Iterable<Integer> createIterable() {
                    return asList(0, 1, 2);
                }
            };
        }
    }

    The feature fixture is implemented like this:

    public class Unmodifiable<T> implements FeatureFixture {
        private final Factory<T> factory;

        public Unmodifiable(Factory<T> factory) {
            this.factory = factory;
        }

        public interface Factory<T> {
            Collection<T> createCollection();
            T createUniqueItem(int id);
        }

        @Test(expected = UnsupportedOperationException.class)
        public void addIsUnsupported() {
            Collection<T> unmodifiable = factory.createCollection();
            unmodifiable.add(factory.createUniqueItem(42));
        }
    }

    The feature test is self contained. Only a factory needs to be supplied. The features are assembled by tagging a public static method creating a factory or a public static field with a @Feature annotations. The runner will ensure that the types of the factory and the annotated feature do match. This is a runtime check, as there is no way to achieve this at compile time because of java’s type erasure.

    The Features Suite runner will get executed because the @RunsWith annotation on top of your test class. JUnit will instantiate it and pass it the Class<?> descriptor of your test. It will get inspected in the same way JUnit does it by using reflection. The gained information will be used to construct explicit FeatureRunner with the feature test Class<?> descriptor and the extracted factory.

    Because the FeatureRunner would execute a group of test cases, I decided to use Suite as a base class for the implementation:

    public class Features extends Suite {
        public Features(Class<?> klass) throws InitializationError {
            super(klass, extractAndCreateRunners(klass));
        }

        private static List<Runner> extractAndCreateRunners(Class<?> klass)
                                                      throws InitializationError {
            List<Runner> runners = new ArrayList<Runner>();
            for (FeatureAccessor field : extractFieldsWithTest(klass)) {
                Class<? extends FeatureFixture> test = field.getFeature();
                runners.add(new FeatureRunner(test, field.getFactory()));
            }
            addSuiteIfItContainsTests(klass, runners);
            return runners;
        }

        private static void addSuiteIfItContainsTests(Class<?> klass,
                                                      List<Runner> runners) {
            try {
                runners.add(new BlockJUnit4ClassRunner(klass));
            } catch (InitializationError e) {// do nothing, no tests
            }
        }


        private static abstract class FeatureAccessor<TField …> {
            private final TField field;
            static <TField extends …> boolean isValid(TField field) {
                return Modifier.isPublic(field.getModifiers())
                        && Modifier.isStatic(field.getModifiers())
                        && field.isAnnotationPresent(Feature.class);
            }

            static <TField …> FeatureAccessor<?> createFrom(final TField field) {
                …
            }

            Class<? extends FeatureFixture> getFeature() {
                return field.getAnnotation(Feature.class).value();
            }
           
            Object getFactory() { 
                …
                return this.field.invoke(null);
            }
        }

        private static List<FeatureAccessor> extractFieldsWithTest(Class<?> klass) {
            List<FeatureAccessor> factoryFieldsWithProperty =
                                     new ArrayList<FeatureAccessor>();
            for (Field field : klass.getFields()) {
                if (!FeatureAccessor.isValid(field)) {
                    continue;
                }
                factoryFieldsWithProperty.add(FeatureAccessor.createFrom(field));
            }
            …
            return factoryFieldsWithProperty;
        }
    }

    The FeatureRunner simply extends JUnits default runner in passing the factory instance into the test constructor:

    class FeatureRunner extends BlockJUnit4ClassRunner {
        private final Object factory;

        FeatureRunner(Class<?> klass, Object factory)
                                              throws InitializationError {
            super(klass);
            if(factory == null){
                throw new InitializationError(…);
            }
            this.factory = factory;
        }

        protected Object createTest() throws Exception {
            return getTestClass().getOnlyConstructor().newInstance(factory);
        }
        …
    }

    Changing my base class fixtures to FeatureFixtures was extremely simple and composing standard unit test bits really is fun now! If you are interested in this solution, the source code is on google-code. I hope it will help you as much as it does help me!

    Monday, October 5, 2009

    NSTL 3.0 released

    Last Friday I finally managed to get the NSTL 3.0 out of the door. Although most of the features were mature and ready more than 3 Months ago, I never managed to finish the automatic build process for the 3.x branch. Well, until last Friday!

    Besides being compiled the first release written using C#3.0 features such as LINQ and extension methods, it offers a lot of functionality to integrate the NSTL and its C++ background seamless into .NET. Actually, using the new extension methods lets you use .NET and NSTL collections and algorithms seamlessly. For instance, you can use .NET collections with iterators in algorithms:

    using.NUnit.Framework;
    using NStl.Linq;//Import the extensions

    List<int> list = new List<int>(){0,1,2,3};
    ListTIterator<int> it = Algorithm.Find(list.Begin(), list.End(), 2);
    Assert.That(it, Is.Not.EqualTo(list.End());

    By being able to pimp up .NET BCL objects with extension methods, I was able to deprecate a whole lot of NSTL containers like Vector<T>, DList<T> and bulky adapter utility methods like NStlUtil.Begin(..).

    Furthermore this release provides extension methods for the NSTL itself, enabling a smooth transition to .NET collections:

    using.NUnit.Framework;
    using NStl.Linq;//Import the extensions

    List<int> list = new List<int>(){0,1,2,3};
    ListTIterator<int> it =
    Algorithm.Find(list.Begin(), list.End(), 2);

    //range contains 0,1
    IEnumerable<int> range = list.Begin().AsEnumerable(it);

    Last but not least you will find LINQ overloads for the Cast<T> operators to cast dictionaries.

    Tuesday, September 8, 2009

    Linq for Java

    I started to work in Java and I’m really missing a comprehensive set of algorithms and data structures. The google collections in combination with the standard collection framework serves the data structure side very well and JGA looks promising for Algorithms. After all I don't want to do another port of the STL :-).

    In the last months working with .NET I started to rely on Linq for my daily work and although it is quite reduced compared to the NSTL, I rarely missed a thing. I especially liked the fluent way that you can manipulate and transform the content any IEnumerable<T>.

    I have found nothing comparable that is as fluent as Linq (JGA is close though) in the Java world and I start to wonder if it is possible to port at least some of the aspects?