首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

11.2  前向推测

在前面的例子中,我们试着从代码中某个特定地点的值的异常情况出发,推断出是哪些对象对它产生影响。然而,在编写特征测试(153页)时,这个过程是反过来的。具体来说就是,面对一组对象,试图搞清如果它们停止工作的话“下游”会发生什么情况。例如,下面这个类来自一个内存中的文件系统。针对它目前还没有任何测试存在,不过我们想要对它作一些修改。

public class InMemoryDirectory {

    private List elements = new ArrayList();

    public void addElement(Element newElement) {

        elements.add(newElement);

    }

    public void generateIndex() {

        Element index = new Element("index");

        for (Iterator it = elements.iterator(); it.hasNext(); ) {

            Element current = (Element)it.next();

          index.addText(current.getName() + "\n");

        }

        addElement(index);

    }

    public int getElementCount() {

        return elements.size();

    }

    public Element getElement(String name) {

        for (Iterator it = elements.iterator(); it.hasNext(); ) {

            Element current = (Element)it.next();

            if (current.getName().equals(name)) {

                return current;

            }

        }

        return null;

    }

}

InMemoryDirectory是一个不大的Java类。我们可以创建一个InMemoryDirectory对象,往其中添加元素(addElement()),生成索引(generateIndex()),并访问其中的元素。这里的元素(Element)即内部存有文本的对象,就像文件一样。生成索引的过程是这样的:创建一个名为“index”的元素,然后将其他所有元素的名字添加到该元素内的文本区中(每个名字一行)。

InMemoryDirectory有一个旧特性,就是generateIndex不能被调用两次,否则就会导致存在两个不同的索引元素(第二个索引创建时实际上是把第一个索引元素当作自己的一般元素了)。

幸运的是,我们的程序使用InMemoryDirectory的方式是非常规矩的。创建目录对象,用元素填充它,调用它的generateIndex方法,然后传递给需要访问它的元素的地方。目前为止一切都好,然而问题是现在我们要进行一次修改,从而允许人们在目录对象的生命周期中的任何时候都可以往里添加元素。

理想情况下,我们希望随着元素被添加进目录,索引的创建和维护工作会自动完成。当第一个元素添加进目录时,索引就应该自动建立起来,而且应当包含被添加元素的名字。下一次,当又有新元素添加进这个目录时,刚才建立的那个索引应该自动被更新以包含这个新元素的名字。看起来给这个新功能编写测试以及满足测试的代码是件再简单不过的事情,但问题是针对目前的行为还没有任何测试存在。那么,我们如何知道将测试安置在哪呢?

本例中答案很清楚:我们需要一系列的测试,它们以各种方式调用addElement,然后生成一个索引,最后获取这些元素看看它们是否正确。问题是,我们怎么知道应该使用这些而不是其他方法呢?本例中问题比较简单,测试只不过是对我们期望的使用目录的方式的描述。我们甚至用不着去看目录类的代码就可以写出这些测试来,因为我们对该类应该做什么早就有很好的把握。不过遗憾的是,有些时候找出合适的测试地点并不是件简单的事。我本可以在这个例子中采用一个大而复杂的类,就像那种常常潜藏在遗留系统中的那些类。但那样一来你可能就会不耐烦地把书给合上了。所以还是让我们假设这是一个棘手的类吧,看看通过考察代码怎样才能弄明白应该测试哪些东西。而对于比这更棘手的问题,这里的推理方式同样适用。

本例中我们首先需要做的事情便是弄清楚该在哪些地方进行修改。答案是我们需要从generateIndex()中移除一些功能,并往addElement()中添加一些功能。确定了这些修改点之后,便可以开始勾勒影响结构示意图了。

首先来看generateIndex()。这个类当中没有任何其他方法调用它,唯一调用它的地方就是客户代码了。那么,另一方面,generateIndex()当中创建了一个新的元素(索引元素)并将该新元素添加进了目录中,它会对该目录类中的元素集合产生影响(见图11-5)。

图11-5  generateIndex 影响了元素集合

现在我们便可以转而考虑元素集合会影响哪些东西了。那么,还有哪些地方使用了元素集合呢?getElementCount似乎算一个,getElement也是。另外,addElement也会用到这个元素集合,不过我们可以不考虑它,因为不管元素集合怎么改变,addElement的行为始终是不变的:也就是说,无论我们对元素集合做什么,addElement的用户都不会受到影响(见图11-6)。

这幅图到这儿算是完成了吗?还没有,我们的修改点是在generateIndex和addElement这两个方法中,因此还需要考察addElement是如何影响周围的系统的。首先,看起来addElement同样会对元素集合造成影响(见图11-7)。

图11-6  generateIndex的改变进一步带来的影响

图11-7  addElement影响元素集合

我们可以进一步看看元素集合的改变会造成哪些影响,不过由于前面我们在分析generateIndex的影响结构时已经这么做过了,所以不再重复。

现在可以画出整个的影响结构示意图了(见图11-8)。

图11-8  InMemoryDirectory类的影响结构示意图

InMemoryDirectory类的用户能够感知到影响的唯一途径便是通过getElementCount和getElement这两个方法。只要我们能够对这两个方法编写测试,似乎也就可以涵盖可能造成的一切影响了。

不过,有没有可能漏掉了什么东西呢?我们考虑过它的基类和派生类吗?如果InMemory- Directory中的某些数据是公有的、受保护的或者包作用域的,那么其派生类中的方法便能以我们所不知道的方式来修改它们。不过,由于本例中InMemoryDirectory中的实例变量是私有的,因此我们无需担心这种情况。

在画影响结构图的时候,你得确保找到了所考察的类的所有客户端。如果你的类有一个基类或派生类,那么得注意一下它们里面是不是还有没有被注意到的客户代码。

到目前为止方方面面都考虑到了吗?还没有,事实上还有一件事情我们一直没有提及。我们的目录类中的元素的类型为Element,但影响示意图中并未出现这个类。下面我们来仔细考察一下它。

generateIndex方法的工作方式是先创建一个Element,然后不断往它里面添加文本。现在让我们来看一看Element类的实现代码:

public class Element {

    private String name;

    private String text = "";

    public Element(String name) {

        this.name = name;

    }

    public String getName() {

        return name;

    }

    public void addText(String newText) {

        text += newText;

    }

    public String getText() {

        return text;

    }

}

所幸的是Element类的定义还算简单。在下面的影响结构图中给generateIndex创建的新元素画一个椭圆(见图11-9)。

图11-9  通过Element类的影响

当新建的元素内部被填充了文本之后,generateIndex便会将它添加进元素集合中,所以,这个新元素影响了元素集合(见图11-10)。

图11-10  generateIndex影响了elements集合

从我们前面的分析中得知,addText方法会影响元素集合,后者又会进一步影响getElement和getElementCount方法的返回值。如果我们想要看看索引元素当中的文本是否正确,可以先用getElement获取它,然后调用其上的getText方法。以上便是所有需要编写测试来侦测修改能带来影响的地方了。

正如前面提到的,这虽是一个相当小的例子,然而却很能代表我们在估计对遗留代码的修改所带来的影响时需要进行的那种推断。要找到安放测试的地点,第一步便是推断出哪儿可以探测到我们的修改所带来的影响,即修改会带来哪些影响。知道了在哪儿能够探测到影响之后,在编写测试的时候便可以在这些地方进行选择了。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论