6

Assume that interface for is defined

interface Foo {
  int getBaz();
  void doBar();
}

Further assume that the contract states that everytime doBar is called baz is incremented. (Ok this is a contrived bit of code but stick with me here)

Now I want to provide a unit test that I can provide to Foo implementers so that they can verify that they meet all the contract conditions.

class FooTest {
  protected Foo fooImpl;

  @Test
  public void int testBazIncrement()
  {
    int b = fooImpl getBaz();
    fooImpl.doBar();
    Assert.assertEquals( b+1, fooImpl.getBaz();
  }
}

What are the best practices for making the test available to the implemnters of Foo? It seems to me that there needs to be a mechanism for them to call the FooTest and provide a Foo or a FooFactory to construct Foo instances. Furthermore, if there are many tests (think big interface) then I want to put all those tests in one FooTest class.

Are there any best practices for how to implement such tests?

mipe34
  • 5,596
  • 3
  • 26
  • 38
cWarren
  • 453
  • 5
  • 11

5 Answers5

2

This is a textbook example of Dependency Injection. If you use Spring as the DI container, you can wire in the fooImpl

@Inject
protected Foo fooImpl;

Your test needs to be annotated with @RunWith(SpringJUnit4ClassRunner.class), and it's up to the interface provider to configure Spring with their implementation.

Without a DI container, you can just create an abstract test class with all the tests in it and an abstract method to provide the implementation object.

protected abstract Foo createFoo();

It's up to the implementation provider to subclass the test and implement the abstract method.

class FooImplTest extends FooTestSuper {

@Override
protected Foo createFoo() {
    return new FooImpl();
}

If you have multiple tests, consider JUnit's @Suite annotation. It's compatible with the Spring test runner.

artbristol
  • 32,010
  • 5
  • 70
  • 103
  • That's OK if you are using dependency injection to supply a single implementation of each interface per project. Would it work for a case more like a *Serializable* interface, that many things must correctly implement'? – soru Jan 08 '13 at 15:34
  • You can have as many Spring configs are there are implementations – artbristol Jan 08 '13 at 15:36
  • Unfortunately, creating all those configs may well be significantly more error-prone that providing a solution in code... – soru Jan 08 '13 at 15:42
  • @Soru With Spring Java config (no XML), it's pretty easy. – artbristol Jan 08 '13 at 15:44
  • This would probably work well in projects that are using IoC frameworks, and as I did not specify that IoC was not being used for the project this seem a good and valid answer. Howerver, in my specific case we are not using IoC for the project itself and adding IoC just for testing seems a bit much. – cWarren Jan 12 '13 at 09:25
0

You can implement a testDataFactory where you istance your objects, or use GSon for create your objects (personally, I like GSon, is clear and fast, you learn it in a few time). For the test implementation, I suggest to write more tests and not a single one. In this way, unit test can be indipendent and you can segregate your problems in a closed structure.

Sonar

Sonar is a tool that help you a lot, making analisys of your code. You can see from sonar front-end how you application is tested:

sonar unit test

as you can see, Sonar can show you where your code is tested or not

Vargan
  • 1,277
  • 1
  • 11
  • 35
  • If there are multiple test case classes, how does the implementer know that they have included all the relevant tests in their testing? If I discover that I missed a test case later and add the test for it how do the implementers know? If the tests are in one file then they will pick up the new tests on the next release. – cWarren Jan 08 '13 at 10:15
  • Sorry, wrong click.. I deleted my previous comment! I edited the response. Personally, I test only the core of my application, when I write a function that has logic inside, well, this part needs to be tested. Also functions that querying the DB needs to be tested, in this way, if you make some mistake you can have a immediate response. – Vargan Jan 08 '13 at 10:30
  • What option you choosed at last? – Vargan Jan 10 '13 at 14:17
0

Why not just have, say, a InterfaceTester that gets called from the unit tests InterfaceImplATest, InterfaceImplBTest, etc?

e.g.

@Test
public void testSerialisation()
{
  MyObject a = new MyObject();

  ...

  serialisationTester.testSimpleRoundTrip(a);

  serialisationTester.testEdgeCases(a);

  ... 
}
soru
  • 5,464
  • 26
  • 30
  • Surely the diagnostics you would get from this would be horrible? – tddmonkey Jan 08 '13 at 15:52
  • No more or less than any other junit test, as far as I can see. Split into smaller @Tests if you really need more detail than 'serialisation is not working for MyObject'. – soru Jan 08 '13 at 16:02
  • The problem with this test case is that the implementation of MyObject needs to change for each of the subclasses. – cWarren Jan 12 '13 at 09:26
0

After much pondering and some dead-ends I have begun using the following pattern:

In the following:

  • [INTERFACE] refers to the interface being tested.
  • [CLASS] refers to the implementation of the interface being tested.

Interface tests are built so that developers may test that implementations meet the contract set out in the interface and accompanying documentation.

The major items under test use an instance of an [INTERFACE]ProducerInterface to create the instance of the object being tested. An implementation of [INTERFACE]ProducerInterface must track all the instances created during the test and close all of them when requested. There is an Abstract[INTERFACE]Producer that handles most of that functionality but requires a createNewINTERFACE implementation.

TESTS

Interface tests are noted as Abstract[INTERFACE]Test. Tests generally extend the Abstract[INTERFACE]ProducerUser class. This class handles cleaning up all the graphs at the end of the tests and provides a hook for implementers to plug in their [INTERFACE]ProducerInterface implementation.

In general to implement a test requires a few lines of code as is noted in the example below where the new Foo graph implementation is being tested.


public class FooGraphTest extends AbstractGraphTest {

        // the graph producer to use while running
        GraphProducerInterface graphProducer = new FooGraphTest.GraphProducer();

        @Override
        protected GraphProducerInterface getGraphProducer() {
                return graphProducer;
        }

        // the implementation of the graph producer.
        public static class GraphProducer extends AbstractGraphProducer {
                @Override
                protected Graph createNewGraph() {
                        return new FooGraph();
                }
        }

}

SUITES

Test suites are named as Abstract[INTERFACE]Suite. Suites contain several tests that excersize all of the tests for components of the object under test. For example if the Foo.getBar() returned an instance of the Bar interface the Foo suite includes tests for the Foo iteself as well as running the Bar tests the Bar. Running the suites is a bit more complicated then running the tests.

Suites are created using the JUnit 4 @RunWith(Suite.class) and @Suite.SuiteClasses({ }) annotations. This has several effects that the developer should know about:

  1. The suite class does not get instantiated during the run.
  2. The test class names must be known at coding time (not run time) as they are listed in the annotation.
  3. Configuration of the tests has to occur during the static initialization phase of class loading.

To meet these requirements the Abstract[INTERFACE]Suite has a static variable that holds the instance of the [INTERFACE]ProducerInterface and a number of local static implementations of the Abstract tests that implement the "get[INTERFACE]Producer()" method by returning the static instance. The names of the local tests are then used in the @Suite.SuiteClasses annotation. This makes creating an instance of the Abstract[INTERFACE]Suite for an [INTERFACE] implementation fairly simple as is noted below.


public class FooGraphSuite extends AbstractGraphSuite {

        @BeforeClass
        public static void beforeClass() {
                setGraphProducer(new GraphProducer());
        }

        public static class GraphProducer extends AbstractGraphProducer {
                @Override
                protected Graph createNewGraph() {
                        return new FooGraph();
                }
        }

}

Note that the beforeClass() method is annotated with @BeforeClass. the @BeforeClass causes it to be run once before any of the test methods in the class. This will set the static instance of the graph producer before the suite is run so that it is provided to the enclosed tests.

FUTURE I expect that further simplification and removal duplicate code can be achieved through the use of java generics, but I have not gotten to that point yet.

cWarren
  • 453
  • 5
  • 11
  • After further work I developed a JUnit suite runner and a set of annotations to annotate that an abstract class is the contract test for the interface. The runner then locates all contract tests for the class in question. For more information see: https://github.com/Claudenw/junit-contracts – cWarren Oct 07 '13 at 15:21
-1

Here are some of my thoughts about how to make a qualified unit test:

First of all, try to make your a implementer class fully tested, which means all of its methods should be covered by the UT. Doing this will save you a lot of time when you need to refactor your code. For your case, it could be :

class FooTest {
    protected Foo fooImpl;

    @Test
    public void testGetBaz() {
    ...
    }

    @Test
    public void testDoBar() {
    ...
    }

}

You will find you need to check the internal state of your class and there is nothing wrong it for UT should be a kind of white-box test.

Second, all methods should be tested separately and not depend on each other. In my opinion, for your code posted above, it looks like more than a function test or an integration test, but it's also necessary.

Third, I don't think it's a good practice to use spring or other container to assemble the target object for you, which will make your test running relatively slow, especially when there a load of tests to run. And your class should intrinsically be pojo and you can complete the assembling in another method in your test class if your target object is really complex.

Fourth, if the parent interface of some class is really big, Dividing the test methods into several groups is something you should do. Here is more info.

Gavin Xiong
  • 957
  • 6
  • 9
  • 1
    I'm afraid this doesn't anser the question - it says nothing about interfaces. Also IMHO contains some significantly bad advice, like isolation testing individual methods. – soru Jan 08 '13 at 15:28
  • @soru Why it's a bad advice to isolate testing methods ? – Gavin Xiong Jan 08 '13 at 16:02
  • 1
    http://stackoverflow.com/questions/333682/tdd-anti-patterns-catalogue, 'The Inspector'. – soru Jan 08 '13 at 16:24