How to test abstract class in Java with JUnit?

JavaJunitAbstract Class

Java Problem Overview


I am new to Java testing with JUnit. I have to work with Java and I would like to use unit tests.

My problem is: I have an abstract class with some abstract methods. But there are some methods which are not abstract. How can I test this class with JUnit? Example code (very simple):

abstract class Car {

    public Car(int speed, int fuel) {
        this.speed = speed;
        this.fuel = fuel;
    }

    private int speed;
    private int fuel;

    abstract void drive();

    public int getSpeed() {
        return this.speed;
    }

    public int getFuel() {
        return this.fuel;
    }
}

I want to test getSpeed() and getFuel() functions.

Similar question to this problem is here, but it is not using JUnit.

In JUnit FAQ section, I found this link, but I don't understand what the author want to say with this example. What does this line of code mean?

public abstract Source getSource() ;

Java Solutions


Solution 1 - Java

If you have no concrete implementations of the class and the methods aren't static whats the point of testing them? If you have a concrete class then you'll be testing those methods as part of the concrete class's public API.

I know what you are thinking "I don't want to test these methods over and over thats the reason I created the abstract class", but my counter argument to that is that the point of unit tests is to allow developers to make changes, run the tests, and analyze the results. Part of those changes could include overriding your abstract class's methods, both protected and public, which could result in fundamental behavioral changes. Depending on the nature of those changes it could affect how your application runs in unexpected, possibly negative ways. If you have a good unit testing suite problems arising from these types changes should be apparent at development time.

Solution 2 - Java

Create a concrete class that inherits the abstract class and then test the functions the concrete class inherits from the abstract class.

Solution 3 - Java

With the example class you posted it doesn't seem to make much sense to test getFuel() and getSpeed() since they can only return 0 (there are no setters).

However, assuming that this was just a simplified example for illustrative purposes, and that you have legitimate reasons to test methods in the abstract base class (others have already pointed out the implications), you could setup your test code so that it creates an anonymous subclass of the base class that just provides dummy (no-op) implementations for the abstract methods.

For example, in your TestCase you could do this:

c = new Car() {
       void drive() { };
   };

Then test the rest of the methods, e.g.:

public class CarTest extends TestCase
{
    private Car c;

    public void setUp()
    {
        c = new Car() {
            void drive() { };
        };
    }

    public void testGetFuel() 
    {
        assertEquals(c.getFuel(), 0);
    }

    [...]
}

(This example is based on JUnit3 syntax. For JUnit4, the code would be slightly different, but the idea is the same.)

Solution 4 - Java

If you need a solution anyway (e.g. because you have too many implementations of the abstract class and the testing would always repeat the same procedures) then you could create an abstract test class with an abstract factory method which will be excuted by the implementation of that test class. This examples works or me with TestNG:

The abstract test class of Car:

abstract class CarTest {

// the factory method
abstract Car createCar(int speed, int fuel);

// all test methods need to make use of the factory method to create the instance of a car
@Test
public void testGetSpeed() {
    Car car = createCar(33, 44);
    assertEquals(car.getSpeed(), 33);
    ...

Implementation of Car

class ElectricCar extends Car {

    private final int batteryCapacity;

    public ElectricCar(int speed, int fuel, int batteryCapacity) {
        super(speed, fuel);
        this.batteryCapacity = batteryCapacity;
    }

    ...

Unit test class ElectricCarTest of the Class ElectricCar:

class ElectricCarTest extends CarTest {

    // implementation of the abstract factory method
    Car createCar(int speed, int fuel) {
        return new ElectricCar(speed, fuel, 0);
    }

    // here you cann add specific test methods
    ...

Solution 5 - Java

I would create a jUnit inner class that inherits from the abstract class. This can be instantiated and have access to all the methods defined in the abstract class.

public class AbstractClassTest {
   public void testMethod() {
   ...
   }
}


class ConcreteClass extends AbstractClass {

}

Solution 6 - Java

You could do something like this

public abstract MyAbstractClass {
    
    @Autowire
    private MyMock myMock;        

    protected String sayHello() {
            return myMock.getHello() + ", " + getName();
    }

    public abstract String getName();
}

// this is your JUnit test
public class MyAbstractClassTest extends MyAbstractClass {

    @Mock
    private MyMock myMock;

    @InjectMocks
    private MyAbstractClass thiz = this;

    private String myName = null;

    @Override
    public String getName() {
        return myName;
    }

    @Test
    public void testSayHello() {
        myName = "Johnny"
        when(myMock.getHello()).thenReturn("Hello");
        String result = sayHello();
        assertEquals("Hello, Johnny", result);
    }
}

Solution 7 - Java

You can instantiate an anonymous class and then test that class.

public class ClassUnderTest_Test {

    private ClassUnderTest classUnderTest;

    private MyDependencyService myDependencyService;

    @Before
    public void setUp() throws Exception {
        this.myDependencyService = new MyDependencyService();
        this.classUnderTest = getInstance();
    }

    private ClassUnderTest getInstance() {
        return new ClassUnderTest() {    
            private ClassUnderTest init(
                    MyDependencyService myDependencyService
            ) {
                this.myDependencyService = myDependencyService;
                return this;
            }

            @Override
            protected void myMethodToTest() {
                return super.myMethodToTest();
            }
        }.init(myDependencyService);
    }
}

Keep in mind that the visibility must be protected for the property myDependencyService of the abstract class ClassUnderTest.

You can also combine this approach neatly with Mockito. See here.

Solution 8 - Java

My way of testing this is quite simple, within each abstractUnitTest.java. I simply create a class in the abstractUnitTest.java that extend the abstract class. And test it that way.

Solution 9 - Java

You can not test whole abstract class. In this case you have abstract methods, this mean that they should be implemented by class that extend given abstract class.

In that class programmer have to write the source code that is dedicated for logic of his.

In other words there is no sens of testing abstract class because you are not able to check the final behavior of it.

If you have major functionality not related to abstract methods in some abstract class, just create another class where the abstract method will throw some exception.

Solution 10 - Java

As an option, you can create abstract test class covering logic inside abstract class and extend it for each subclass test. So that in this way you can ensure this logic will be tested for each child separately.

Solution 11 - Java

You don't need a fancy Mockito add on, or anonymous classes, or whatever other things the other answers are recommending. Junit supports test classes extending each other: so, write a thorough, abstract test class (literally just make the test class abstract) for your abstract base class, that examines how each of the methods behave. Do these tests on a set of instance-variable objects, that are set up as you desire in an @BeforeEach method in this base test class. Have that method call an abstract allocateObjects() method, which will do all the object allocation.

Then, for each class that extends your abstract base, have a test class that extends the abstract test class you just wrote. This test class will do the actual object allocation in the overridden allocateObjects() method. The objects it allocates will be used by the methods in the parent test class: methods which are inhertied by this test class, and therefore run as a part of its testing.

Could you do factory tomfoollery? I guess: but since you probably need to create test classes for each subclass anyways, you might as well just keep things simple with inheritence. I suppose if you have a lot of subclasses, and none of them do anything that is worth testing appart from the superclass stuff, it would be worth it: but why on earth would you be creating subclasses in that case?

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionvascoView Question on Stackoverflow
Solution 1 - Javansfyn55View Answer on Stackoverflow
Solution 2 - JavaKevin BowersoxView Answer on Stackoverflow
Solution 3 - JavaGrodriguezView Answer on Stackoverflow
Solution 4 - Javathomas.mc.workView Answer on Stackoverflow
Solution 5 - JavamartingView Answer on Stackoverflow
Solution 6 - JavaiilView Answer on Stackoverflow
Solution 7 - JavaSamuelView Answer on Stackoverflow
Solution 8 - JavaHaominView Answer on Stackoverflow
Solution 9 - JavaDamian Leszczyński - VashView Answer on Stackoverflow
Solution 10 - JavaAndrew TaranView Answer on Stackoverflow
Solution 11 - JavaCalum McConnellView Answer on Stackoverflow