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

相关推荐
sukalot3 小时前
Windows 图形显示驱动开发-WDDM 3.2-用户模式工作提交(二)
windows·驱动开发
charlie1145141913 小时前
IMX6ULL驱动开发Linux篇02——移植Rootfs
linux·驱动开发·学习·内核·rootfs·教程·移植
触角0101000114 小时前
OLED屏幕开发全解析:从硬件设计到物联网显示实战 | 零基础入门STM32第五十二步
驱动开发·stm32·单片机·嵌入式硬件·物联网
亭墨15 小时前
linux0.11内核源码修仙传第五章——内存初始化(主存与缓存)
linux·c语言·驱动开发·学习·缓存·系统架构
亭墨21 小时前
linux0.11内核源码修仙传第四章——head.s
linux·驱动开发·学习·系统架构
VermouthSp1 天前
Linux驱动开发(1.基础创建)
linux·驱动开发
charlie1145141912 天前
从0开始的操作系统手搓教程24——完成我们的键盘驱动子系统
驱动开发·操作系统·键盘·手搓教程
迅为电子3 天前
【北京迅为】iTOP-RK3568OpenHarmony系统南向驱动开发GPIO基础知识
驱动开发·openharmony·rk3568
charlie1145141913 天前
IMX6ULL驱动开发uboot篇01
驱动开发·嵌入式硬件·uboot·imx6ull
charlie1145141913 天前
从0开始的操作系统手搓教程23:构建输入子系统——实现键盘驱动1——热身驱动
驱动开发·学习·计算机外设·操作系统·教程·键盘