Run same Junit Test with multiple objects derived from same interface

Viewed 434

I am trying to improve my knowledge about testing I'm trying to achieve running the same JUnit test class with different objects derived from the same interface.

so we can assume the following:

interface Base {
    void sort();
}

class  A implements Base {
    @Override
    public void sort() {
        //sort naively
    }
}

class  B implements Base {
    @Override
    public void sort() {
        //sort using another better approach
    }
}

class  C implements Base {
    @Override
    public void sort() {
        //sort using optimized approach
    }
}
class Test {
    @Test
    void test1() {

        Base obj = new A();
        obj.sort();
        obj.otherStuff();
    }
}
class SecondTest {
//trying to avoid making multiple test classes that has only one line in difference
@Test
void test1() {
    var obj = new B();
    obj.sort();
    obj.otherStuff();
}

So my question is how to run the test class with the objects from A,B,C without falling into the trap of duplicate code and redundancy?

Please note that I wrote this example just to illustrate my point, and the sort() doStuff() methods are just placeholders but when you have over ~70 line of code duplication in each test class it starts to look ugly and redundant.

*I have looked at @beforeEach, @Before, @After and I don't think I see a way where those might help me.

3 Answers

A way to fix it is the following, you create a method within your test class that takes as input the Base obj and contains all the duplicate lines. What you'll do then is to initialize the obj in different tests, then pass it to the method. Here is a code that would do the job:

class Test {
@Test
void test1() {

    Base obj = new A();
    wrapperMethod(obj);
}
@Test
void test2() {
   var obj = new B();
   wrapperMethod(obj);
 }

public static void wrapperMethod(Base obj){
obj.sort();
obj.otherStuff();
}
}

As a rule of thumb, testing can be much like normal programming where redundancy is avoided with methods to guarantee reusability.

Cheers,

D

You can write a parameterized test with a MethodSource.

@ParameterizedTest
@MethodSource("bases")
@Test
void test1(Base obj) {
  obj.sort();
  obj.otherStuff();
}

static Stream<String> bases() {
  return Stream.of(new A(), new B(), new C());
}

First of all you have to fix your understanding of what UnitTesting is about.

UnitTesting is not about code (coverage).

UnitTesing is about verifying desired public behavior where "public behavior means return values and/or communication with dependencies. Each test method should verify a single atomic assumption of the tested units desired behavior.

From this point of view it does not make sense to pass a bunch of objects sharing the same interface trough the same test method since these different interface implementations exist to implements the interfaces methods with their own unique behavior. In turn the assumption how the objects behave differ uniquely.

If all the objects where expected to behave identically (which is the only assumption a single test method could verify) there where no different objects (i.e. implementations) in the first place.

Related