我有一些管理类,用于搜索方法,添加,更改和删除方法,以表格格式打印,并将映射写入文件方法。这些类还有一个容器作为属性。假设有一个类X.这将是类XManagement,其容器具有类X的对象。
search()方法返回X的对象,但首先它通过输入收集它的ID。
add()方法收集用于创建对象X的输入数据,其代码的最后一行用于将该对象添加到其容器中。
change()方法首先搜索用户想要更改的对象(通过search()方法),然后收集数据并通过setter方法更改对象。然后它调用write()方法重写文件。
delete()方法搜索对象(通过search()),然后将其从容器中删除,之后调用write()方法。
write()方法也是无效的。它遍历每个对象的容器,然后将其数据附加到可解析的String,该String将写入文件。
以下是示例:
public class XManagement {
protected Hashtable<Integer, X> xes = new Hashtable<>();
public XManagement(String fileName) {
// Constructor.
// Loads the input file, then parses it.
// Once parsed, the objects of X class are created.
// They are then put into the container (xes).
}
protected X search() {
// Both generic methods.
Integer uuid = enterInteger("ID");
return (X) find(uuid, xes);
}
public void add() {
Integer uuid = UUID(xes); // Generic method, generates UUID.hashCode()
// and checks for duplicates.
String a = enterString("Name");
Date d = enterDate("Start");
// ...............
X x = new X(uuid, a, d, etc);
xes.put(x.getID(), x);
write();
}
public void delete() {
X x = search();
xes.remove(x.getID(), x);
write();
}
public void change() {
X x = search();
String a = enterString("Name");
x.setA(a);
Date d = enterDate("Start");
x.setD(d);
// .......................
write();
}
protected void write() {
File file = new File("x.txt");
BufferedWriter out = new BufferedWriter(new FileWriter(file));
String curr = "";
for (int id : xes.keySet()) {
curr += xes.get(id).getA() + "|" + xes.get(id).getD() + "|"; // etc
}
out.write(curr);
// There's, naturally, try/catch/finally here. For the sake of simplicity, I left it out here.
}
}
X类是这样的:
public class X {
String a;
Date d;
// etc
public X(String a, Date d) {
this.a = a;
this.d = d;
}
// Getters and setters.
}
这比这复杂得多,我只是想在这里保持简单以获得一些帮助 - 当我得到基础时,我会试着找出更难的东西。
在某些管理类中,方法和构造函数将其他管理类的实例作为其输入参数,以便它们可以在其中调用其方法,因为它们中的大多数都是连接的。假设Y类将X作为属性,当我在YManagement add()方法中创建Y对象时,我需要能够通过xes中包含的search()方法从xes中的所有可用X对象中选择一个。 XManagement。
我现在决定保持简单,但如果你愿意,你可以告诉我如何进行测试,其中我将其他管理类的实例作为输入。
如何为这些方法编写详细的JUnit 5测试用例?
很抱歉,如果我在代码的某个地方犯了一个错误,我没有复制它,但写在这里,概括了在其他管理类中重复的内容。
如果您对代码本身有任何其他建议,请随时写一下。
答案 0 :(得分:2)
这些方法难以测试,因为它们做得太多了。您有输入,输出到文件和数据修改。
让我们来看看这个方法:
protected X search() {
// Both generic methods.
Integer uuid = enterInteger("ID");
return (X) find(uuid, xes);
}
为什么在将所需的ID作为参数传递给方法时,可以调用enterInteger
?让客户告诉您的班级要搜索哪个ID。现在搜索正在做一件事:在地图中查找引用。
我认为命名一个X类没有提供任何关于它是什么的信息。我更喜欢能给我提示的东西 - 更好的可读性。您使用此命名方案从代码中抽象出所有信息。好名字很重要。对此更加思考。
您的XManagement
类看起来像一个简单的内存数据库。您是否考虑过使用允许您使用SQL的东西?也许H2会是更好的选择。如果这个类是基于接口的,你可以换掉实现,客户端也不必改变。
更好的设计会将责任划分为单独的类。例如,您的数据对象可能伴随着一个基于接口的持久层,可以处理搜索,更新,持久性等。
当我发现方法太难以测试时,通常表明该类需要重新设计。难以测试与客户难以使用的相同。
我用接口替换你的XManagement
类:
package persistence;
public interface Repository<K, V> {
List<V> find();
V find(K id);
List<V> find(Predicate<V> filter);
void save(V v);
void update(V v);
void delete(K id);
void delete(V v);
}
您的每个节目,演出,门票,用户等都有一个实例。
package persistence;
public class ShowRepository implements Repository<Integer, Show> {
// TODO: You'll need a constructor and a Map for Shows.
public List<Show> find() { // the rest for you }
public Show find(Integer id) { // the rest for you }
public List<Show> find(Predicate<Show> filter) { // the rest for you }
public void save(Show v) { // the rest for you }
public void update(Show v) { // the rest for you }
public void delete(Integer id) { // the rest for you }
public void delete(Show v) { // the rest for you }
}
在我看来,比你的X好多了。
如果您使用我的界面编写课程,则不会在这些课程中进行任何控制台交互。它需要的一切都由来电者传递。
您可以为内存缓存创建单独的具体实现,每个实现此接口的关系或NoSQL数据库。
答案 1 :(得分:1)
你的问题相当广泛
所以,我将专注于必要的。
1)如何测试void
方法?
void
方法不会返回任何结果,但会对基础对象/系统产生副作用。
所以你必须断言void方法通过声明预期的副作用是有效的来完成它的设计。
例如,您的add()
方法会在HashTable
中添加对象(如果您有竞争条件,则应该使用HashMap
或ConcurrentHashMap
),因此您应该检查对象被正确添加。
例如,您可以使用search()
方法返回包含对象的对象。通过使用它,您可以检查是否添加了对象:
X x = ...;
xManagement.add(x);
X actualX = xManagement.search(x.getId());
assertEquals(x, actualX)
要做到这一点,你必须让你的实际课程进化,而实际上并没有提供简单的检索方法。
2)如何测试与其他类有依赖关系的类?
班级的单元测试应该与其他班级分开进行
因此,如果YManagement
方法必须调用XManagement
的方法,则应该模拟XManagement
依赖关系并为其记录行为。
不要测试两次相同的东西。
答案 2 :(得分:1)
您需要重新设计代码,因为当前的实现是不可测试的。我建议采取以下步骤:
之后,您将能够使用模拟依赖项或伪造对象测试您的类。查看SOLID principles,就像您关注它们一样,您的代码将是可测试且可维护的。