译释Dozen ways to find bugs(6)——Learn by checked example
Posted on 2009-02-28 17:13 Aaron Wu 阅读(179) 评论(0) 收藏 举报
     学习第三API是一件让人很头疼的事情,如果够走运,将学习的API会附带比较详细的介绍文档;如果API会像附带文档所介绍的那样使用,那你的运气显然已经不是一般般地好了。对于我自己来讲,我习惯于边用边学,这样对于文档的好坏的依赖性就减弱了很多。为了确认一个API到底实现了什么样的功能,我尝试在代码中调用这个API,来揣测这个API的功能然后验证自己的猜想。往往在刚刚开始做这些尝试的时候,我们会觉得这个过程很曲折,但是我想告诉你的是:探索式学习是一种很有效的学习方法。
<imports omitted for brevity>
        public class LuceneLearningTest extends TestCase {
public void testIndexedSearch() throws Exception {
        //
        // Prepare a writer to store documents in an in-memory index.
        //
        Directory indexDirectory = new RAMDirectory();
        IndexWriter writer = 
        new IndexWriter(indexDirectory, new StandardAnalyzer(), true);
        //
        // Create a document to be searched and add it to the index.
        //
        Document document = new Document();
        document.add(Field.Text("contents", "Learning tests build confidence!"));
        writer.addDocument(document);
        writer.close();
        //
        // Search for all indexed documents that contain a search term.
        //
        IndexSearcher searcher = new IndexSearcher(indexDirectory);
        Query query = new TermQuery(new Term("contents", "confidence"));
        
        Hits hits = searcher.search(query);
        assertEquals(1, hits.length());
    }
}
         LuceneLearningTest是一个很常见的JUnit测试类,它调用了Lucene的API以便为一个处于RAMDirectory中的示例文件建立索引,并断言在这个文档中单词“confidence”出现了一次。我们可以一次建立一批类似的测试方法,每一个我们不懂的地方我们都可以用这种方法去学习了解,我们为之写一个测试方法并要求持续重构——把一些通用的部分放到setUp()方法中去。下面是一个经过了重构了的LuceneLearningTest类,相对于第一个示例来讲,我又添加了两个查询类型:
<imports omitted for brevity>
public class LuceneLearningTest extends TestCase {
    private IndexSearcher searcher;    
    public void setUp() throws Exception {
        Directory indexDirectory = new RAMDirectory();
        IndexWriter writer = 
            new IndexWriter(indexDirectory, new StandardAnalyzer(), true);
            Document document = new Document();
            document.add(Field.Text("contents", "Learning tests build confidence!"));
            writer.addDocument(document);
writer.close();
            searcher = new IndexSearcher(indexDirectory);
    }
    public void testSingleTermQuery() throws Exception {
        Query query = new TermQuery(new Term("contents", "confidence"));    
        Hits hits = searcher.search(query);
        assertEquals(1, hits.length());
    }
    
    public void testBooleanQuery() throws Exception {
        Query query = 
            QueryParser.parse("tests AND confidence", "contents", new StandardAnalyzer());
            Hits hits = searcher.search(query);
           assertEquals(1, hits.length());
    }
    public void testWildcardQuery() throws Exception {
        Query query = 
            QueryParser.parse("test*", "contents", new StandardAnalyzer()); 
        Hits hits = searcher.search(query);
        assertEquals(1, hits.length());
    }
}
         读者应该已经注意到,我们已经将建立索引的方法重构到了setUp()方法中去了(SetUp方法是在每一个测试方法TestMethod()运行之前调用的)。这样,SetUp()方法有四种作用:
    》减少测试方法中的代码重复
    》保证测试方法不依赖也不影响其他的测试方法
    》有助于读者了解我们这个测试类的目的:建立索引和搜索。
    》提醒你:在你的程序中使用搜索功能的频率比建立索引频率高
    在一个孤立的环境下面写探索性测试方法可以帮助我们将精力集中在一个点上。在一开始,我们把精力集中在对于一个API的理解学习上面,然后我们再写我们利用该API开发的应用程序。当这些测试方法通过了,我们就可以将这个API集成到我们的应用程序中。(这样做得意义在于确保我们使用的API准确无误地提供了我们想要利用的功能,译者注。)换句话说,就是要一点点建立起我们(对于这个API)的信心。如果这个API的某些行为发生了改变,相对于集成测试方法来讲这些探索式测试方法可以帮助我们更加精确地定位问题所在。
    当一个API的新版本发布出来之后,我们怎么确认它是否还适合我们的应用程序呢?有了探索性学习得来的测试方法就好办了,我们可以像运行回归测试一样来运行这些测试方法,这样我们就可以很快了解到新版本与旧版本相比发生了哪些改变。在我们使用一个新版的API的时候,我们需要运行这些探索性测试方法来保证我们之前对于这个API的断言还是正确的。(保证我们的使用的API方法还是我们之前需要的那个样子,译者注。)
require 'test/unit'
class RubyArrayTest < Test::Unit::TestCase
  def testPushPopShift
    a = Array.new
    a.push("A")
    a.push("B")
    a.push("C")
    assert_equal(["A", "B", "C"], a)
    assert_equal("A", a.shift)
    assert_equal("C", a.pop)
    assert_equal("B", a.pop)
    assert_equal(nil, a.pop)
  end
  
  def testCollect
    a = ["H", "A", "L"]
    collected = a.collect { |element| element.succ }
    assert_equal(["I", "B", "M"], collected)
  end
end
每次当我需要记起某个API或者Ruby语言的使用方法的时候,我回去查看我当初写的那些探索性测试。这些方法会告诉我那些API或者语言特性怎样使用,这样我才会有更大的把握来保证自己是在朝着正确的方向前进。如果在已有的测试方法中我找不到想要的,那么我就利用这种探索学习方法去学习新的知识。
 
                     
                    
                 
                    
                 
         
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号