Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it ok to unit-test an abstract class by instantiating it as a mock object?

It has come to my attention that you can unit-test an abstract class by instantiating it as a mock object. Thereby you mock the abstract properties and methods, while being able to test the implemented ones.

However, I am accustomed to distinguish between the class to test and the injected dependencies to mock/stub. So, in this early stage of my new enlightenment I wonder if there are any pitfalls to this way of testing?? Any thoughts on this?

Regards, Morten

like image 348
Morten Avatar asked Apr 12 '11 08:04

Morten


3 Answers

An abstract class, to be of any value, should have concrete subclasses which can be instantiated. So unit test those, and via them implicitly the base class.

If it has no concrete subclasses, I can't think of any reason why it should exist (as an abstract class, that is).

In general, I prefer to use mocking only to set up the environment of the class to be tested, not to instantiate the class itself. This distinction - to me - keeps the test cases clearer, and ensures that I always test the real functionality of the class.

Of course, I can think of cases (with legacy code) when the main issue is to be able to write unit tests somehow, anyhow, to enable refactoring (as discussed in Working Effectively with Legacy Code). As a temporary solution in such cases, (almost) anything goes. But once the unit tests are working, the class should be refactored properly asap to make it clean and testable (and its unit tests too, to make them maintainable).

like image 72
Péter Török Avatar answered Nov 01 '22 21:11

Péter Török


Instead of using inheritance to combine two classes, see if you can use composition.

So instead of this:

abstract class PaySalary {
    payAmount(decimal money) {
        if (transferAmount(money)) {
            isPaid = true;
        }
    }

    abstract void transferAmount(decimal money);
}

Do this:

class PaySalary {
    public MoneyTransferrer;

    payAmount(decimal money) {
        if (MoneyTransferrer.transferAmount(money)) {
            isPaid = true;
        }
    }
}

This way, you don't have to inherit the class in your unit test, but you can simply mock a MoneyTransferrer. You can also test the MoneyTransferrer without depending on the base class.

like image 27
Sjoerd Avatar answered Nov 01 '22 20:11

Sjoerd


If there isn't a concrete implementation in the part I'm testing, I usually write a test-implementation. In the unit test I do something similar as the user of my classes does. If he inherits my class, I inherit in the test.

On the other side, I wouldn't say that it is bad to use a mock framework to create an implementation. In my cases I usually doesn't work, usually because I need to write ORM mapping files for that class and thing like this.

like image 38
Stefan Steinegger Avatar answered Nov 01 '22 20:11

Stefan Steinegger