第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测试框架的理解更加全面,在测试驱动开发的道路上又迈进了一步。后续我们将继续探索如何更好地处理测试失败等情况,进一步提升测试框架的稳定性和可靠性。

相关推荐
驱动探索者4 小时前
瑞芯微 RK3588 平台驱动开发 学习计划
驱动开发·学习
花小璇学linux1 天前
imx6ull-驱动开发篇16——信号量与互斥体
linux·驱动开发·嵌入式软件
花小璇学linux1 天前
imx6ull-驱动开发篇15——linux自旋锁
linux·驱动开发·嵌入式软件
花小璇学linux2 天前
imx6ull-驱动开发篇14——原子操作
linux·驱动开发·嵌入式软件
sukalot2 天前
window显示驱动开发—验证覆盖支持
驱动开发
程序员JerrySUN3 天前
当前主流GPU全景讲解:架构、功能与应用方向
数据库·人工智能·驱动开发·redis·缓存·架构
猫猫的小茶馆4 天前
【STM32】HAL库中的实现(二):串口(USART)/看门狗(IWDG/WWDG)/定时器(TIM)
arm开发·驱动开发·stm32·单片机·嵌入式硬件·mcu·智能硬件
sukalot4 天前
window显示驱动开发—覆盖 DDI
驱动开发
花小璇学linux4 天前
imx6ull-驱动开发篇10——pinctrl 子系统
linux·驱动开发·imx6ull·嵌入式软件
sukalot4 天前
window显示驱动开发—可选内容保护 DDI 函数
驱动开发