第21章 走进 xUnit:测试驱动开发的关键工具(又续)

写在前面


这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后,我突然思考,对于测试开发工程师来说,什么才更有价值呢?如何让 AI 工具更好地辅助自己写代码,或许优质的单元测试是一个切入点。 就我个人而言,这本书确实很有帮助。第一次读的时候,很多细节我都不太懂,但将书中内容应用到工作中后,我受益匪浅。比如面对一些让人抓狂的代码设计时,书里的方法能让我逐步深入理解代码的逻辑与设计。 作为一名测试开发工程师,我想把学习这本书的经验分享给大家,希望能给大家带来帮助。因为现在工作中大多使用 Python 代码,所以我把书中JAVA案例都用 Python 代码进行了改写 。


在前面的博客中,我们深入探讨了xUnit测试框架里测试的资源清理、日志记录以及tearDown方法的实现等内容。接下来,我们将继续探索书中关于测试计数以及处理测试失败等方面的知识,并通过完整的示例代码来呈现。

测试计数功能的实现

目标与需求

我们打算实现一个功能,确保无论测试方法出现何种异常,tearDown方法都会被调用,并且希望捕获这些异常并记录到测试结果中。同时,我们希望TestCase.run()方法返回一个TestResult对象,用于记录测试运行的结果,包括运行的测试数目和失败的测试数目等信息。

代码实现过程

  1. 伪实现阶段
    我们先从伪实现开始,定义TestResult类并提供一个简单的summary方法:
python 复制代码
class TestResult:
    def summary(self):
        return "1 run, 0 failed"

同时,更新TestCase类的run方法,使其返回TestResult对象:

python 复制代码
class TestCase:
    def __init__(self, name):
        self.name = name

    def setUp(self):
        pass

    def tearDown(self):
        pass

    def run(self):
        self.setUp()
        method = getattr(self, self.name)
        method()
        self.tearDown()
        return TestResult()
  1. 完善TestResult类
    为了准确记录测试数目,我们需要对TestResult类进行改进。将runCount初始化为0,每运行一个测试就将其加1:
python 复制代码
class TestResult:
    def __init__(self):
        self.runCount = 0

    def testStarted(self):
        self.runCount = self.runCount + 1

    def summary(self):
        return "%d run, 0 failed" % self.runCount
  1. 更新TestCase类的run方法
    在TestCase类的run方法中,调用testStarted方法来更新测试计数:
python 复制代码
class TestCase:
    def __init__(self, name):
        self.name = name

    def setUp(self):
        pass

    def tearDown(self):
        pass

    def run(self):
        result = TestResult()
        result.testStarted()
        self.setUp()
        method = getattr(self, self.name)
        method()
        self.tearDown()
        return result
  1. 测试失败的情况
    为了测试测试失败时的情况,我们编写一个新的测试testFailedResult,以及在WasRun类中定义一个会抛出异常的testBrokenMethod方法:
python 复制代码
class TestCaseTest(TestCase):
    def testFailedResult(self):
        test = WasRun("testBrokenMethod")
        result = test.run()
        assert "1 run, 1 failed" == result.summary()


class WasRun:
    def __init__(self, name):
        self.name = name
        self.wasRun = None
        self.log = ""

    def setUp(self):
        self.wasRun = None
        self.log = "setUp "

    def testMethod(self):
        self.wasRun = 1
        self.log = self.log + "testMethod"

    def tearDown(self):
        self.log = self.log + " tearDown"

    def testBrokenMethod(self):
        raise Exception

目前,上述测试会存在问题,因为我们还没有捕获由WasRun.testBrokenMethod抛出的异常,并将其作为测试失败的结果记录到测试结果中。

完整示例代码

python 复制代码
class TestResult:
    def __init__(self):
        self.runCount = 0
        self.failureCount = 0

    def testStarted(self):
        self.runCount = self.runCount + 1

    def testFailed(self):
        self.failureCount = self.failureCount + 1

    def summary(self):
        return "%d run, %d failed" % (self.runCount, self.failureCount)


class TestCase:
    def __init__(self, name):
        self.name = name

    def setUp(self):
        pass

    def tearDown(self):
        pass

    def run(self):
        result = TestResult()
        result.testStarted()
        self.setUp()
        try:
            method = getattr(self, self.name)
            method()
        except:
            result.testFailed()
        self.tearDown()
        return result


class WasRun:
    def __init__(self, name):
        self.name = name
        self.wasRun = None
        self.log = ""

    def setUp(self):
        self.wasRun = None
        self.log = "setUp "

    def testMethod(self):
        self.wasRun = 1
        self.log = self.log + "testMethod"

    def tearDown(self):
        self.log = self.log + " tearDown"

    def testBrokenMethod(self):
        raise Exception


class TestCaseTest(TestCase):
    def testFailedResult(self):
        test = WasRun("testBrokenMethod")
        result = test.run()
        assert "1 run, 1 failed" == result.summary()

本章小结

回顾本章内容,我们完成了以下重要任务:

  • 编写了一个伪实现,并逐步将其完善,以变量代替常量来准确记录测试运行的相关信息。
  • 编写了针对测试功能的测试用例,即测试的测试,用于验证测试框架的正确性。
  • 针对测试运行失败的情况编写了专门的测试用例,虽然目前还存在异常捕获的问题,但为后续完善测试框架指明了方向。

通过不断地深入研究和实践,我们对xUnit测试框架的理解更加全面,在测试驱动开发的道路上又迈进了一步。后续我们将继续探索如何更好地处理测试失败等情况,进一步提升测试框架的稳定性和可靠性。

相关推荐
dump linux1 小时前
内核驱动调试接口与使用方法入门
linux·驱动开发·嵌入式硬件
济6175 小时前
ARM Linux 驱动开发篇----Linux驱动开发与裸机开发的区别---- Ubuntu20.04
linux·arm开发·驱动开发
嵌入式-老费5 小时前
Linux camera驱动开发(mipi、d/cphy、dsi和csi2)
驱动开发
测试人社区—03926 小时前
UI测试在DevOps流水线中的卡点设计:质量保障的智能防线
运维·驱动开发·测试工具·ui·ar·vr·devops
罗马尼亚硬拉6 小时前
tensile/index.rst
驱动开发
TangDuoduo00059 小时前
【Linux字符设备驱动】
linux·驱动开发
Max_uuc10 小时前
【C++ 硬核】摆脱开发板:用 Google Test + Mock 构建嵌入式 TDD (测试驱动开发) 体系
驱动开发·tdd
小龙报11 小时前
【51单片机】串口通讯从入门到精通:原理拆解 + 参数详解 + 51 单片机实战指南
c语言·驱动开发·stm32·单片机·嵌入式硬件·物联网·51单片机
dlz08361 天前
POE驱动开发流程
驱动开发
嵌入式-老费1 天前
Linux camera驱动开发(DVP接口的camera sensor)
驱动开发