如何正确使用abstract 类(测试有问题)
How to correctly use abstract classes (problems with testing)
我遇到了抽象 classes 的一些问题,这是测试它们的问题(代码的公共部分没有创建具体实现),我想问一下应该如何解决这个问题问题正确。
我需要 class 表示我从 PLC 读取的变量的当前状态(我在这里使用观察者模式),我还需要写入其中一些变量。我希望这部分独立于连接(OPC UA、MODBUS ...)这就是我创建 class PlcData
的原因,它代表我需要写入或读取的所有变量,这是一个抽象 class 我想根据连接类型创建不同的实现 (PlcDataOpcua
, PlcDataModbus
...)
但是当我尝试编写单元测试时遇到了这个问题,我无法测试所有 PlcData
.
之间共享的公共代码
基于第二个最受好评的答案 here 我想到了这样的事情:
但是因为 Connection
需要更新 PlcData
中的信息,它需要跟踪 PlcData
实例。对我来说,它比原来的解决方案更复杂,但当然它是“可测试的”。
谁能告诉我该走哪条路或想出更好的解决方案?
您可以使用 Mockito 来测试 Abstract classes:
https://www.baeldung.com/junit-test-abstract-class
测试具体的 classes 可能是更好的方法
(编写一个足以测试任一连接的通用测试 class)
PLC 数据和连接类型现在以一种干净的方式分开。我经常看到像你原来的那样的设计。他们试图通过继承“通用案例”来实现可交换的功能。过了一会儿,他们变得非常凌乱。
要使 Connection
能够更新 PlcData
中的数据,您应该考虑观察者模式,它使您能够保持连接解耦。这些连接将提供注册“回调”的方法,这些回调将在某些状态更改时调用。如果数据量很小,可以在调用中传递,或者回调函数会“掉头”从连接中拉取数据。
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() {
...
}
}
您还可以创建并行测试用例 class 层次结构。看我的回答here.
我遇到了抽象 classes 的一些问题,这是测试它们的问题(代码的公共部分没有创建具体实现),我想问一下应该如何解决这个问题问题正确。
我需要 class 表示我从 PLC 读取的变量的当前状态(我在这里使用观察者模式),我还需要写入其中一些变量。我希望这部分独立于连接(OPC UA、MODBUS ...)这就是我创建 class PlcData
的原因,它代表我需要写入或读取的所有变量,这是一个抽象 class 我想根据连接类型创建不同的实现 (PlcDataOpcua
, PlcDataModbus
...)
但是当我尝试编写单元测试时遇到了这个问题,我无法测试所有 PlcData
.
基于第二个最受好评的答案 here 我想到了这样的事情:
但是因为 Connection
需要更新 PlcData
中的信息,它需要跟踪 PlcData
实例。对我来说,它比原来的解决方案更复杂,但当然它是“可测试的”。
谁能告诉我该走哪条路或想出更好的解决方案?
您可以使用 Mockito 来测试 Abstract classes: https://www.baeldung.com/junit-test-abstract-class
测试具体的 classes 可能是更好的方法 (编写一个足以测试任一连接的通用测试 class)
PLC 数据和连接类型现在以一种干净的方式分开。我经常看到像你原来的那样的设计。他们试图通过继承“通用案例”来实现可交换的功能。过了一会儿,他们变得非常凌乱。
要使 Connection
能够更新 PlcData
中的数据,您应该考虑观察者模式,它使您能够保持连接解耦。这些连接将提供注册“回调”的方法,这些回调将在某些状态更改时调用。如果数据量很小,可以在调用中传递,或者回调函数会“掉头”从连接中拉取数据。
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() {
...
}
}
您还可以创建并行测试用例 class 层次结构。看我的回答here.