Author: root

  • 研究:Python Mock Object教程

    原文连接:http://www.xper.org/wiki/seminar/PythonMockObjectTutorial

    ===============

    源文件可以在mockobject.py处下载(它使用到了PyUnit模块)。

    在开始之前,我想先和大家确定一点:本教程不是Mock Objects的介绍;它只是一个如何在Python 中使用MockObject^模块的简短介绍。在这里我们会看到一些Mock Objects的基本用法。要了解Mock Objects更多的信息,可以参见:

    第一篇文章是任何想使用Mock Objects的人的必读(囧——译者注)。

    JuNe

    假定你和你所在的团队在建立一个KMS。那么应该有一个数据库保存了所有的文档。团队决定创建一个函数来删除所有一年前发布的文章。根据文档名删除文档,以及取得所有文档名和发布日期的功能是已经有了的——由接口完成——但还没有实现,数据库也没有建立。你被分配来开发“删除老文档”的功 能,但它取决于数据库组件。你是不是应该等下去,直到别人完成了你的模块所依赖的所有功能后才进行开发呢?

    让我们使用Mock Objects。 第一个版本将是这样的:

    import unittest
    def removeOldDocs(aDb):
        pass
    class RemoveOldDocs(unittest.TestCase):
        def testRemoveNoneFromEmptyDb(self):
            removeOldDocs(db)
    if __name__=='__main__':
        unittest.main(argv=('','-v'))

    此时,我们想测试的情况是:如果数据库中没有任何文档,那么也就没有任何文档会被删除——这可能是最简单的情况了。这里有两重的问题。他们还没有实现数据库,而我们如何能测试removeOldDocs表现正常呢?让我们用一个mock object来进行“伪装”。

    import unittest,mockobject
    class MockDb(mockobject.MockObject):
        pass
    def removeOldDocs(aDb):
        pass
    class RemoveOldDocs(unittest.TestCase):
        def testRemoveNoneFromEmptyDb(self):
            db=MockDb()
            removeOldDocs(db)

    现在正常了,但是测试代码并不完整。我们需要测试removeOldDocs的行为。在测试用例中增加以下两行:

    class RemoveOldDocs(unittest.TestCase):
        def testRemoveNoneFromEmptyDb(self):
            db=MockDb()
            db._setExpectedRemoveCalls(0)
            removeOldDocs(db)
            db._verify()

    当然,此时会出错——因为我们还没有定义_setExpectedRemovedCalls。还好,计算机告诉我们接下来做什么:定义这个方法。 我们在新定义的方法中要做些什么呢?我们应该设置删除调用应该进行的次数。因此,我们在MockDb__init__中创建一个Expectation对象。

    class MockDb(mockobject.MockObject):
        def __init__(self):
            self._removeCalls=mockobject.ExpectationCounter(MockDb.removeCalls)
        def _setExpectedRemoveCalls(self,aCount):
            self._removeCalls.setExpected(aCount)

    现在我们运行测试代码,代码测试通过。现在,让我们做一些有意义的事情。

        def testRemoveOne(self):
            db=MockDb()
            db._addDoc(doc1,1992/3/1)
            db._setExpectedRemoveCalls(1)
            removeOldDocs(db)
            db._verify()

    等一下。当前时间的信息在哪里?我们是不是也可以“伪装”它?是的。你可以替换time模块(或函数)而改用MockTime类。但是,这不是一个很好的想法。为什么不用参数将它进行传递呢?哈,我们需要修改代码,以及测试用例。让我们暂时注释掉刚加入的测试用例,让所有的测试都得以通过。然后修改测试代码。

    class RemoveOldDocs(unittest.TestCase):
        def testRemoveNoneFromEmptyDb(self):
            db=MockDb()
            db._setExpectedRemoveCalls(0)
            removeOldDocs(db,2002/3/1)
            db._verify()
    ##    def testRemoveOne(self):
    ##        db=MockDb()
    ##        db._addDoc(doc1,1992/3/1)
    ##        db._setExpectedRemoveCalls(1)
    ##        removeOldDocs(db)
    ##        db._verify()

    如我们所愿,测试出错了。让我们使之通过:增加一个参数。

    def removeOldDocs(aDb,aDate):
        pass

    现在回到刚才被注释掉的测试用例。函数调用中增加一个参数并运行。这回,计算机需要定义一个新的方法。

    class MockDb(mockobject.MockObject):
    ......
        def _addDoc(self,aName,aDate):
            pass

    现在又出错了。需要调用一次删除操作,但是一次也没有调用。

    class MockDb(mockobject.MockObject):
        def getAllDocumentDates(self):
            return ()
    def removeOldDocs(aDb,aDate):
        for eachDoc,eachDate in aDb.getAllDocumentDates():
            if isOlderThanBy(eachDate,aDate,365):
                aDb.remove(eachDoc)

    假定isOlderThanBy已经实现了。这行的意思是:如果eachDate早于aDate超过365天。

    现在我们创建一个新的属性以便我们在mock db中保存测试用的文档。

    class MockDb(mockobject.MockObject):
        def __init__(self):
            self._removeCalls=mockobject.ExpectationCounter(MockDb.removeCalls)
            self._docs={}
        def _setExpectedRemoveCalls(self,aCount):
            self._removeCalls.setExpected(aCount)
        def _addDoc(self,aName,aDate):
            self._docs[aName]=aDate
        def getAllDocumentDates(self):
            return self._docs.items()

    是时候在mock db中加入remove方法了。

    class MockDb(mockobject.MockObject):
        def remove(self,aName):
            self._removeCalls.inc()

    现在所有都可以执行通过。我们可以再增加一个测试用例。

        def testRemoveSome(self):
            db=MockDb()
            db._addDoc(doc1,1992/2/15)
            db._addDoc(doc2,1992/3/15)
            db._addDoc(doc3,1992/4/15)
            db._setExpectedRemoveCalls(2)
            removeOldDocs(db,1993/4/1)
            db._verify()

    不需要修改代码,该用例应该可以通过。

    如果你想确定某些特定文档被删除,你可以使用ExpectationSet。例如:

    class MockDb(mockobject.MockObject):
        def __init__(self):
            self._removeCalls=mockobject.ExpectationCounter(MockDb.removeCalls)
            self._removedDocs=mockobject.ExpectationSet(MockDb.removedDocs)
            self._docs={}
        def _addExpectedRemovedDoc(self,aName):
            self._removedDocs.addExpected(aName)
        def remove(self,aName):
            self._removeCalls.inc()
            self._removedDocs.addActual(aName)
            #you may add del self._docs[aName] here if you'll use getAllDocumentDates again
    class RemoveOldDocs(unittest.TestCase):
        def testRemoveSome(self):
            db=MockDb()
            db._addDoc(doc1,1992/2/15)
            db._addDoc(doc2,1992/3/15)
            db._addDoc(doc3,1992/4/15)
            db._setExpectedRemoveCalls(2)
            db._addExpectedRemovedDoc(doc1)
            db._addExpectedRemovedDoc(doc2)
            removeOldDocs(db,1993/4/1)
            db._verify()

    有很多不同类型的Expectation类,如ExpectationCounterExpectationListExpectationSet以及ExpectationMap等。

    尽管我们在测试用例中创建了mock objects,但是使用setUp方法等将它们重构出去会更好。而且我们有很大的几率消除代码中的重复。代码会更短、更清晰,只要我们严格遵循OAOO。但可惜的是,本教程就会更加的冗长了。

  • BT群5月份签名档记录

    2008-05-05 10:44:19 我是出来买酱油的
    嗯,事不关己,高高挂起。
    2008-05-06 11:06:10 :据报道,目前S群情绪稳定
    源于性浪的一篇新闻报道:目前S者情绪稳定。可不吗,这世界上已经没有比S者情绪更稳定的人了。不过不知道是不是一语成谶……
    2008-05-13 09:55:26 为大地震死难者默哀。顺大便BS决定继续传递火炬的SB
    汶川大地震于5月12日发生。针对火炬接力如期举行的举措,我们顺大便表示了我们的不满。
    2008-05-16 08:45:18 道义放两旁,关怀摆中间
    针对救生、救灾过程中发生的一些道德问题,BT群适时的提出了这个口号。实践证明,我们这次又跑在了国务院之前。
    2008-05-19 09:28:19 为地震死难者默哀
    全国哀悼日。
    2008-05-22 09:40:10 继续生活,继续工作,继续关怀
    生活还要继续……

    五月份因有长假和地震的原因,签名更换不多。

  • “六一”苏州乐园游

    今天是“六一”国际儿童节,圣上和梓童、太子一起到苏州乐园游玩。

    去的比较早,所以还比较空,到了下午就不对了,这个people mountain people sea啊。

    这次去,除了是应“六一”的景之外,也是为了测试圣上的四能加百五零弟。联机相册已经创建于此处,欢迎鉴定。

    ============

    从拍摄效果看,四能加百五零弟还是不错的,特别是连拍,拍到了梓童在弹射升空时哇哇乱叫、花容失色的珍贵镜头,但由于涉及暴力和形象问题,所以只放一张,另加一张抓拍的悬挂式过山车的照片。

    太子照片当然还是重点。Hiahiahia。

  • Wag the Dog

    Why the dog wags its tail?
    Because the dog is smarter than its tail.
    If the tail is smarter than the dog,
    The tail will wag the dog.

    ===================

    猛禽在我的大力推荐下,终于看完了Conspiracy Theory,而我则在11年后,再次重新欣赏了有两位Oscar金像奖得主——达斯汀·霍夫曼和罗伯特·德·尼罗——出演的标准美国式黑色幽默片:Wag the Dog。

    (more…)

  • 湖人夺取NBA西部冠军

    如我所预料的,湖人没有浪费任何赛点,在第四场“破发”而获得赛点后,又干脆利索的在第五场战胜马刺,从而获得西部总冠军。

    首先,要感谢马刺,他们的坚韧不拔使得本场比赛,以及整个西部决赛充满了紧张和刺激——而这些紧张和刺激是次次体位不能忍受的。

    其次,科比真真正正的体现了一个MVP的价值。他也极有可能成为NBA历史上为数不多的双料MVP之一——马刺的邓肯就是其中之一。

    加索尔毕竟还需要历练,他在针对对方不同的中锋的时候的表现反差还是太大。不过,庆幸的是,东部无论是凯尔特人还是活塞,在中锋位上都不会对他的表现产生太多的威胁。

    鉴于此,我认为在总决赛中,湖人将更充分的发挥KG组合,而不是KO组合。

    大胆预测:总决赛湖人将以4:2的战绩加冕总冠军,科比将毫无争议的荣膺季后赛MVP称号。

  • 老彼得戴上了红领巾

    经过昨天意外的推迟,今天星海学校终于进行了一年级入队仪式,而老彼得也理所当然、不可不戒的成为第一批光荣的少先队员,戴上了红领巾。

    他很高兴,以致于回家拿下红领巾后,在出门学习钢琴之前,又郑重的戴上了它,说要给王老师看看。

    嗯,还是很有荣誉感的。

    放张照片给大家瞅瞅:

  • 湖人很好,很强大

    很抱歉,我对湖人拿下本场的决心还是低估了。洛杉矶人显然不希望在主场才拿到赛点,而是在客场就拿到赛点,从而彻底粉碎马刺的任何侥幸的想法:前面打成3:3,然后在第七场博一博,就象他们对付黄蜂那样。

    但是显然,洛杉矶人不想给他们这样妄想的机会。

    3:1,湖人拿到了赛点,西部决赛将在第五场结束,湖人又获得了宝贵的休息时间,从而开始他们最大的考验:第一次在没有主场优势的情况下,挑战东部。

    另,今日看到消息,央视将暂停NBA转播。特此留存纪念。

  • 湖人与马刺

    湖人在与马刺的客场第一战中发挥失常。今天会迎来第四场的比赛。

    回顾湖人和掘金、爵士的比赛,我个人认为湖人今天胜算不大。

    湖人会确保在自己的主场赢得赛点,然后象对付爵士那样在第六场一举将马刺拔下。

    纯属猜测。

  • 总结时间(五)

    继续我自己的总结时间。

    ===============

    地震已经发生了半个月,但是无论是物理上的余震还是心理上的余震还远远没有平息。

    各界人士都伸出了援助之手,以不同的方式帮助着受苦的人儿。

    对于媒体的一些言论,我并不是很满意。

    (more…)

  • BT群帐篷捐赠明细

    BT群帐篷捐赠明细如下:

    支出:

    • 帐篷一顶:2100
    • 吴江-上海过路费:100
    • 总计:2200

    收入:

    • 猎手 200
    • 小超夫妇 210
    • 群群 150
    • 魔铃 300
    • Jessie 100
    • 令狐 240
    • 猛禽+yili:600
    • TR+GR:400
    • 总计:2200

    (more…)