90

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() ;
JimHawkins
  • 4,107
  • 8
  • 28
  • 53
vasco
  • 1,374
  • 1
  • 14
  • 18
  • 4
    See http://stackoverflow.com/questions/1087339/using-mockito-to-test-abstract-classes for two solutions using Mockito. – ddso Sep 27 '11 at 12:58
  • Is there any advantage to learn another framework for testing? Is Mockito only an extension to jUnit, or completely different project? – vasco Sep 27 '11 at 13:25
  • Mockito does not replace JUnit. Like other mocking frameworks, it is used in addition to a unit testing framework and helps you with creating mock objects to use in your test cases. – ddso Sep 27 '11 at 13:31
  • 1
    Language agnostic: http://stackoverflow.com/questions/243274/best-practice-with-unit-testing-abstract-classes – Ciro Santilli新疆棉花TRUMP BAN BAD Feb 06 '15 at 08:41

10 Answers10

107

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.

nsfyn55
  • 13,511
  • 7
  • 45
  • 75
  • You are absolutely right. I didn't think about it in this way. I just thought it is good to have test suit for every class in project, so I can be sure every class is working how I suppose. – vasco Sep 27 '11 at 13:22
  • 17
    100% code coverage is a myth. You should have exactly enough tests to cover all your known hypotheses about how your application should behave(preferably written before you write the code la Test Driven Development). I am currently working on a very highly functioning TDD team and we only have 63% coverage as of our last build all written as we developed. Is that good? who knows?, but I would consider it a waste of time to go back and try to boost that higher. – nsfyn55 Sep 27 '11 at 13:34
  • I would avoid the test duplication and declare methods as final where it applies. The flip side to this argument, is that you will have to change multiple tests if you decide to refactor or change the function of the abstract class – HandyManDan Oct 03 '13 at 01:45
  • 3
    sure. Some would argue that is a violation of good TDD. Imagine you are on a team. You make the assumption that the method is final and don't put tests for in in any concrete implementations. Someone removes the modifier and makes changes that ripple down an entire branch of the inheritance hierarchy. Wouldn't you want your test suite to catch that? – nsfyn55 Oct 05 '13 at 11:35
  • 32
    I disagree. Whether you work in TDD or not, your abstract class's concrete method contains code, therefore, they should have tests (regardless of whether there are subclasses or not). Moreover, unit testing in Java tests (normally) classes. Therefore, there's really no logic in testing methods that are not a part of the class, but rather of its super class. Following that logic, we should not test any class in Java, except for classes with no sub classes at all. Regarding the methods being overridden, that's exactly when you add a test to check the changes/additions to the sub class's test. – ethanfar Sep 17 '14 at 09:54
  • 1
    @eitanfar I can see where you disagree, but an abstract class is not a class its a template for a class. It isn't anything worth testing till you extend it. – nsfyn55 Sep 17 '14 at 14:24
  • 3
    @nsfyn55 What if the concrete methods were `final`? I don't see a reason for testing the same method multiple times if the implementation *can't* change – Dioxin Apr 09 '15 at 02:29
  • 1
    That's great if you are working by yourself. What if I'm working with you and decide that I don't like that method being `final` anymore? – nsfyn55 Apr 09 '15 at 13:48
  • 3
    Shouldn't we have the tests target the abstract interface so we can run them for all implementations? If it's not possible, we'd be violating Liskov's, which we'd want to know about and fix. *Only* if the implementation adds some extended (compatible) functionality should we have a specific unit test for it (and just for that extra functionality). – tne Feb 19 '16 at 15:09
  • Inheritance is a complex subject. LSP only applies to the signature. Think of this pseudo code method that takes an `int` and always returns `True` its signature is `int-->bool` if I substituted with a method that takes an `int` and always returns `False` its still `int-->bool`, LSP is intact, but I've fundamentally changed the behavior in this case by making it work the exact opposite way. If you can do some wizardry testing the abstract interface is probably ok for `final` methods, but anything else you are gambling. – nsfyn55 Feb 19 '16 at 17:56
  • @nsfyn55 ... I disagree. _Abstraction_ is one of the key methods of code reuse. Unit testing is a core process to verify low-_er_ level components in order to establish (a) the reusable (abstract) code is _correct_ and (b) unit tests of child-classes are correct and we are Not testing parent-class behaviour. .. .. .. That said, we must choose our battles, there's only so much time to write test-cases. _Hmmm_; NO, Correctness tests make time. – will Jul 13 '16 at 14:57
  • @eitanfar .. .. .. I think I see your point; however that assertion is Language dependent. In Java or C# an interface is the template specification. An _abstract class_ in C++, C#, Java, or a mixin in Ruby -- These are often partial-classes that DO _things_. On the _olde_ days languages like Simula and Algol-68 didn't have '_abstraction_' just classes and abstract classes grew out of the practice of writing **reusable** chunks of code in antecedent classes. Personally I find _antecedent_ and _descendant_ gives much better semantics than '_parent_' – will Jul 13 '16 at 15:08
  • @nsfyn55 LSP does not only apply to the signature but also to the behavior. See wiki definition: "...objects of type T may be replaced with objects of type S **without altering** any of the desirable properties of T (**correctness, task performed, etc.**). LSP is a particular definition of a subtyping relation, called (strong) **behavioral subtyping**" – František Žiačik Feb 23 '17 at 15:12
  • @FrantišekŽiačik alright I'll grant that I may not have looked that closely at LSP but the strict definition borders on the philosophical. The commenters argument of "testing the abstract interface" falls into a grey area that I would personally avoid. There is no hard/fast rule about overriding the behavior of an abstract class, if you are persuasive enough you could even assert that LSP is maintained. FWIW All of this can be avoided and proper test suites written by delegating to `static` methods or relying on composition rather than inheritance. – nsfyn55 Feb 28 '17 at 01:40
  • I completely agree with @eitanfar. If there is a unit of code, there should be some automated test independent if the class is abstract or not. – rafaelim Aug 16 '17 at 14:23
  • Well that makes a handful of you. At the time of this comment 74 people disagreed with you. – nsfyn55 Aug 21 '17 at 13:54
  • I think we're talking past each other, people. Take this perspective: you should test all concrete subtypes using a single test suite written to target their supertype. We're not testing the abstract classes, we're testing the implementations and whether they implement it correctly (catching LSP violations which as noted above is about way more than just method signatures). Of course *if* subtypes add compatible behavior of their own, it should *also* be tested in *other* test suites specific to these subtypes. Incompatible behavior? Don't use inheritance. Does *anybody* disagree with that? – tne Nov 26 '17 at 20:20
  • What if it's different developers groups that write abstract class and concrete implementations? Is it Ok for the "abstract" group to ship their code with 0% test coverage? – Alexander Oct 09 '18 at 21:34
37

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

Kevin Bowersox
  • 88,138
  • 17
  • 142
  • 176
  • What would you do in case that you have 10 concrete classes extending the abstract class and each of these concrete classes would implement just 1 method and let's say that other 2 methods are the same for each of these classes, because they are implemented in the abstract class? My case is that I do not want to copy-paste the tests for abstract class to each subclass. – scarface Aug 12 '20 at 14:27
12

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.)

Grodriguez
  • 20,528
  • 10
  • 53
  • 97
  • Thanks for answer. Yes, my example was simplified (and not so good). After reading all answers here, I wrote dummy class. But as wrote @nsfyn55 in his answer, I write test for every descendant of this abstract class. – vasco Sep 28 '11 at 11:44
9

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
    ...
thomas.mc.work
  • 6,234
  • 2
  • 22
  • 40
5

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);
    }
}
iil
  • 144
  • 1
  • 3
4

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 {

}
Garis M Suero
  • 7,603
  • 7
  • 39
  • 68
marting
  • 57
  • 2
  • 3
    This is excellent advice. It could be improved by providing an example, though. Perhaps an example of the class you're describing. – SDJMcHattie Mar 04 '14 at 07:05
2

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.

Samuel
  • 2,120
  • 1
  • 19
  • 31
2

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.

Haomin
  • 479
  • 5
  • 9
0

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.

0

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.