2

I came over some problem with abstract classes and it is that there is problem to test them (the common part of the code without creating concrete implementation) and I would like to ask how should solve this problem correctly.

I need to have class representing current status (I use observer pattern here) of variables which I read from PLC and I also need to write to some of these variables. I would like this part to be connection independent (OPC UA, MODBUS ...) That's why I created class PlcData which represents all variables I need to write or read, this was an abstract class and I wanted to create different implementations based on connection type (PlcDataOpcua, PlcDataModbus ...)

But when I was trying to write unit tests I came over this problem, that I can not test the common code shared between all PlcData.

Based on the second most upvoted answer here I came up with something like this: enter image description here

But because the Connection needs to update informations in PlcData it needs to keep track of the PlcData instance. For me it seams more complicated than the original solution but of course it would be "testable".

Could anyone tell me which way to go or came up with better solution?

3 Answers3

0

You can use Mockito to test Abstract classes: https://www.baeldung.com/junit-test-abstract-class

Testing the concrete classes might be a better way to go (Write a single test class that's generic enough to test either connection)

Will_Q
  • 1
  • Hi, I already read this article but I have few problems with it. It seams it is not "good practice" based on link I posted and the biggest problem is that it does't work for me, my methods changes inner variables and it doesn't seem to work with mockito (tests ends with null pointer exception) For now I created anonymous class which leaves the methods which should be over written empty (this works but it is not nice) I would also like to know what is the correct (based on best practises) colution. – Jakub Znamenáček Feb 22 '21 at 14:46
0

The PLC data and the connection type are now separated in a clean way. I often see designs like your original one. They try to implement exchangable functionality by inheriting from the "common case". After a while they get really messy.

To enable the Connections to update data in PlcData you should consider the observer pattern, which enables you to keep the connections decoupled. The connections would provide ways of registering "callbacks" that would be called on certain state changes. If the amount of data is small, it could be passed along in the call or the callback function would "turn around" and pull the data from the connection.

interface Connection {
    void registerLevelChange(Runnable callback);
}

class ModBusConnection implements Connection {
    private List<Runnable> levelChangeCallbacks = new ArrayList<Runnable>();

    public void registerLevelChange(Runnable callback) {
        levelChangeCallbacks.add(callback);
    }

    void processIncomingData() {
        if (level.hasChanged()) {
            for (var cb: levelChangeCallbacks) {
                cb.run();
            }
        }
    }
}

class PlcData {
    private Connection connection;

    void someMethod() {
        ...
        connection.registerLevelChange(() -> handleLevelChange());
        ...
    }    

    void handleLevelChange() {
        ...
    }
}
EricSchaefer
  • 22,338
  • 20
  • 63
  • 99
  • Hi, I agree with you that it is nicer to have connection separated but what you proposed isn't applicable to my problem. I will create new question how to implement it correctly and link it here It would really help me If you could check my problem and help me there. – Jakub Znamenáček Feb 24 '21 at 09:05
  • Here is my updated question [link](https://stackoverflow.com/questions/66355002/how-to-replace-abstract-class-composition-instead-of-inheritance-concrete-p) – Jakub Znamenáček Feb 24 '21 at 17:01
0

You can also create a parallel test case class hierarchy. See my answer here.

Ray Tayek
  • 9,151
  • 6
  • 40
  • 77