练习 47 - 自动化测试 - 笨办法学Python3

练习 47. 自动化测试

为了确认游戏功能是否正常,你需要一遍一遍地在你的游戏中输入命令。这个过程非常枯燥。如果能写一小段代码来测试你的代码岂不是更好?一旦你对程序做了任何修改,或者添加了什么新东西,你只要“跑一下你的测试”,这些测试就能确保程序依然能正常运行。这些自动测试不会捕捉到所有 bug,但是可以让你无需重复输入命令来运行你的代码,从而为你节约很多时间。

从这一节开始,以后每个练习将不再有“你会看到”这一部分,取而代之的是“你应该测试”(What You Should Test)部分。从现在开始,你需要为自己写的所有代码写自动化测试,这会让你成为一个更好的程序员。

我不会试图解释为什么你需要写自动化测试。我要告诉你的是,你想要成为一个程序员,而程序的作用是让无聊冗繁的工作自动化,测试软件毫无疑问是无聊冗繁的,所以你还是写点代码让它为你来做测试工作比较好好。

这应该是你需要的所有的解释了。因为你写单元测试的原因是让你的大脑更加强健。你读了这本书,写了很多代码让它们实现一些事情。现在你将更进一步,写出能读懂你写的其他代码的代码。这个写代码来测试你写的其他代码的过程将强迫你清楚地理解你之前写的代码。同时清晰地了解这些代码实现的功能及其原理,从而让你对细节的注意更上一个台阶。

写一个测试用例(test case)

我们会拿一段非常简单的代码为例,写一个简单的测试,这个测试将建立在上节我们创建的项目骨架上面。

首先,从你的项目骨架创建一个叫做 ex47 的项目。以下是你要遵循的步骤,我会通过语言描述来告诉你,而不是直接给你代码,这样可以给你思考的机会:

  1. 把项目骨架复制到 ex47。

  2. 把所有的 NAME 文件重命名为 ex47。

  3. 把所有文件中的 NAME 单词替换为 ex47。

  4. 最后,删除所有 *.pyc 文件来确保你的文件夹是干净的。

如果你卡住了,可以回去查阅练习 46,如果没办法完成这些步骤,那你可能需要多练习几次。

警告!

记住,你通过运行 nosetests 命令来运行测试。你可以直接输入 python3.6 ex47_tests.py 来运行,但是没那么容易跑通,而且你需要为每个测试文件执行一次这个命令。

接下来,创建一个简单的文件 ex47/game.py ,你可以把代码放入其中进行测试。这是一个非常小的类,其代码如下:

game.py

1   class Room(object): 
2
3       def __init__(self, name, description):
4           self.name = name
5           self.description = description
6           self.paths = {} 
7
8       def go(self, direction):
9           return self.paths.get(direction, None) 
10
11      def add_paths(self, paths):
12          self.paths.update(paths)

一旦你有了这个文件,把单元测试骨架改成这样:

ex47_tests.py

1   from nose.tools import *
2   from ex47.game import Room 
3
4
5   def test_room():
6       gold = Room("GoldRoom",
7                   """This room has gold in it you can grab. There's a
8                   door to the north.""")
9       assert_equal(gold.name, "GoldRoom")
10      assert_equal(gold.paths, {}) 
11
12  def test_room_paths():
13      center = Room("Center", "Test room in the center.")
14      north = Room("North", "Test room in the north.")
15      south = Room("South", "Test room in the south.") 
16
17      center.add_paths({'north': north, 'south': south})
18      assert_equal(center.go('north'), north)
19      assert_equal(center.go('south'), south) 
20
21  def test_map():
22      start = Room("Start", "You can go west and down a hole."
23      west = Room("Trees", "There are trees here, you can go east.")
24      down = Room("Dungeon", "It's dark down here, you can go up.")
25
26      start.add_paths({'west': west, 'down': down})
27      west.add_paths({'east': start})
28      down.add_paths({'up': start}) 
29
30      assert_equal(start.go('west'), west)
31      assert_equal(start.go('west').go('east'), start)
32      assert_equal(start.go('down').go('up'), start)

这个文件引入了你在 ex47.game 模块中的 Room 类,这样你就能在这上面进行测试。然后是一系列以 test_ 开头的函数来进行的测试。在每一个测试用例中都有一小段代码,它们会创建一个或多个房间,然后去确认房间的功能和你期望的是否一样。它先测试了基本的房间功能,然后测试了路径,最后测试了整个地图。

这里最重要的函数是 assert_equal ,它保证了你设置的变量,以及你在 Room 里设置的路径和你的期望相符。如果你得到错误的结果,nosetests 将会打印出一个错误信息,这样你就可以找到出错的地方并修正过来。

测试指南

在测试时,你可以照着下面这些不是很严格的指南来做:

  1. 测试脚本要放到 tests/ 目录下,并且命名为 BLAH_tests.py ,否则 nosetests 就不会执行你的测试脚本了。这样做还有一个好处就是防止测试代码和别的代码互相混淆。
    ai酱注: 这里的 BLAH_tests.py 是一种调皮的写法(BLAH是废话的意思),你应该把 BLAH 替换为你的 NAME,在这个练习中就是 ex47

  2. 为你创建的每个模块写一个测试。

  3. 测试用例(函数)尽量保持简短,但如果看上去不怎么整齐也没关系,测试用例一般都有点乱。

  4. 就算测试用例有些乱,也要试着让他们保持整洁,把里边重复的代码删掉。创建一些辅助函数来避免重复的代码。当你下次在改完代码需要改测试的时候,你会感谢我这一条建议的。重复的代码会让修改测试变得很难操作。

  5. 最后一条是别太把测试当回事。有时候,更好的方法是把代码和测试全部删掉,然后重新设计代码。

你会看到

Exercise 47 会话

$ nosetests
...
-----------------------------------------------------------------
Ran 3 tests in 0.008s OK

如果一切正常的话你应该会看到这个。试着搞一个错误,看看输出结果是什么,然后再把代码修改正确。

附加练习

  1. 阅读 nosetests 相关文档,再去了解一下其他替代方案。

  2. 了解一下 Python 的 “doc tests”,看看你是不是更喜欢这种测试方式。

  3. 改进你游戏里的 Room,然后用它重建你的游戏,这次重写,你需要一边写代码,一边把单元测试写出来。

常见问题

我运行 nosetests 的时候收到了一个语法错误(syntax error) 如果你收到这样的提示,看一下错误提示是怎么说的,然后修改出错的哪一行或者之前的一行。像 nosetests 这样的工具是运行你的代码和测试代码,所以它们会在运行 Python 的同时发现语法错误。

我为什么没办法引入 ex47.game ? 确认你创建了 ex47/__init__.py 文件,回到练习 46 看看如何创建。如果问题不是出在这儿,那么你可以这样做:
macOS/Linux 系统:

export PYTHONPATH=.

Windows 系统:

$env :PYTHONPATH = "$env :PYTHONPATH ; . "

最后,确保你是用 nosetests 来进行测试,而不是在用 Python。

我运行 nosetests 的时候看到了 UserWarning 你可能装了两个版本的 Python,或者你不是用的 distribute,回去跟着练习 46 安装一下 distribute 或者 pip 就可以了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容