Java:如何测试void添加,删除和更改方法?

时间:2017-08-15 12:48:32

标签: java unit-testing junit

我有一些管理类,用于搜索方法,添加,更改和删除方法,以表格格式打印,并将映射写入文件方法。这些类还有一个容器作为属性。假设有一个类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测试用例?

很抱歉,如果我在代码的某个地方犯了一个错误,我没有复制它,但写在这里,概括了在其他管理类中重复的内容。

如果您对代码本身有任何其他建议,请随时写一下。

3 个答案:

答案 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中添加对象(如果您有竞争条件,则应该使用HashMapConcurrentHashMap),因此您应该检查对象被正确添加。
例如,您可以使用search()方法返回包含对象的对象。通过使用它,您可以检查是否添加了对象:

X x = ...;
xManagement.add(x);
X actualX = xManagement.search(x.getId());
assertEquals(x, actualX)

要做到这一点,你必须让你的实际课程进化,而实际上并没有提供简单的检索方法。

2)如何测试与其他类有依赖关系的类?

班级的单元测试应该与其他班级分开进行 因此,如果YManagement方法必须调用XManagement的方法,则应该模拟XManagement依赖关系并为其记录行为。
不要测试两次相同的东西。

答案 2 :(得分:1)

您需要重新设计代码,因为当前的实现是不可测试的。我建议采取以下步骤:

  • 将代码分解为更具凝聚力的类;
  • 提取接口;
  • 对提供的类使用依赖注入;
  • 使用参数化方法;

之后,您将能够使用模拟依赖项或伪造对象测试您的类。查看SOLID principles,就像您关注它们一样,您的代码将是可测试且可维护的。