1. 2006-03-21 校对记要

校对文件: roman.xml

章节第13章

说明粗体是修改的位置,斜体是原文及翻译,斜粗体是校对后的修改

探讨:本章后三节的标题的翻译似乎不妥,但我也没有想出更好的译法。

  • 13.4. Testing for success

    成功测试法

    为成功而测试?

    13.5. Testing for failure

    失败测试法

    为失败而测试?

    13.6. Testing for sanity

    回旋测试(Testing for sanity)

    完整性测试 ?

  • 辛苦!不过仔细的校对记录是下次翻译的指南针!
    • 以上三组测试应该有关联性的,可以是 正测试/反测试/稳健测试 意思应该是 善意测试/暴力求失败测试/正常操作测试? --ZoomQuiet

  • 我和Jason 的讨论
    • me: 那几个标题,还是不好翻译。公司里的QA告诉我,他们一般也不去想中文该怎么说。而且给了一个新的说法:Testing for success他们一般叫positive testing,Testing for failure 叫 negative testing。而sanity,他们从来都直接说sanity check。写到这儿的时候,我突然想到"完备性检测"似乎比较合适。
    • Jason: Sanity 译作“完备性检测”挺贴切。Testing for success/failure 按照 positive/negative testing 翻译为“正面/负面测试”感觉不错!
  • 继续讨论。。。
    • 同意!“完备性检测、正面/负面测试” -- ZoomQuiet

1.1. 第13章第1节 罗马数字程序介绍 II

http://www.woodpecker.org.cn/obp/diveintopython-zh-5.4/zh-cn/dist/htmlflat/diveintopython.html#roman.intro

1st paragraph 2nd sentence:

  • Now that you have some Python under your belt, you're going to step back and look at the steps that happen before the code gets written.

  • 现在你以对 Python 更多的了解将回顾这个程序并从其代码开发之前入手。

  • 既然你已对Python 有了一定的了解,那么接下来将回顾这个程序并从其代码被开发之前入手。

2nd paragraph 1st sentence:

  • In the next few chapters, you're going to write, debug, and optimize a set of utility functions to convert to and from Roman numerals.

  • 在接下来的几章中,你将会编写,纠错以及优化一系列使用函数来进行罗马数字和阿拉伯数字之间的转换。

  • 在接下来的几章中,你将编写、调试和优化一系列工具函数来进行罗马数字和阿拉伯数字之间的转换。

roman.py 功能需求 中所有的valid翻译成“有效”invalid翻译成“无效”较翻译成“合法”“非法”合适。

需求第6点:

  • 6. If you take a number, convert it to Roman numerals, then convert that back to a number, you should end up with the number you started with. So fromRoman(toRoman(n)) == n for all n in 1..3999.

  • 将一个数转换为罗马数字表示,再转换回阿拉报数字表示后应该和最初的数相同。因此,fromRoman(toRoman(n)) == n 对于 1..3999 之间所有 n 都适用。

  • 将一个数转换为罗马数字表示,再转换回阿拉伯数字表示后应该和最初的数相同。因此,fromRoman(toRoman(n)) == n 对于 1..3999 之间所有 n 都适用。

需求第7点:

  • 7. toRoman should always return a Roman numeral using uppercase letters.

  • toRoman 返回的罗马数字应该适用大写字母。

  • toRoman 返回的罗马数字应该使用大写字母。

last sentence between parentheses

  • short answer: haphazardly and inconsistently

  • 简言之:充满偶然性和异然性

  • 简言之:充满偶然性和反复无常

1.2. 第13章第2节 深入

http://www.woodpecker.org.cn/obp/diveintopython-zh-5.4/zh-cn/dist/htmlflat/diveintopython.html#roman.divein

1st paragraph:

  • Now that you've completely defined the behavior you expect from your conversion functions, you're going to do something a little unexpected: you're going to write a test suite that puts these functions through their paces and makes sure that they behave the way you want them to. You read that right: you're going to write code that tests code that you haven't written yet.

  • 现在你已经定义了你的转换程序所应有的功能,下面一步有点令你出乎意料:你将要开发一个测试组建将使用你未来的函数以确保它们工作正常。没错:你将为还未开发的程序开发测试代码。

  • 现在你已经定义了你的转换程序所应有的功能,下面一步会有点儿出乎你的意料:你将要开发一个测试组件(test suite)来测试你未来的函数以确保它们工作正常。没错:你将为还未开发的程序开发测试代码。

2nd paragraph:

  • This is called unit testing, since the set of two conversion functions can be written and tested as a unit, separate from any larger program they may become part of later. Python has a framework for unit testing, the appropriately-named unittest module.

  • 这就是所谓的单元测试,由于这两个转换程序组成可以被当作一个单元共同开发和测试,而不去考虑他们可能今后成为一个大程序的一部分。 Python 有一个单元测试框架,被恰如其分地称作 unittest 模块。

  • 这就是所谓的单元测试,因为这两个转换函数可以被当作一个单元来开发和测试,不用考虑它们可能今后成为一个大程序的一部分。 Python 有一个单元测试框架,被恰如其分地称作 unittest 模块。

hints:

  • unittest is included with Python 2.1 and later. Python 2.0 users can download it from pyunit.sourceforge.net.

  • unittest 已被包含于 Python 2.1 和之后的版本中。 Python 2.0 用户则可以从 pyunit.sourceforge.net下载。

  • Python 2.1 和之后的版本已经包含了unittest。Python 2.0的用户则可以从 pyunit.sourceforge.net下载。

3rd paragraph:

1st sentence

  • Unit testing is an important part of an overall testing-centric development strategy.

  • 单元测试是核心测试开发技巧的一部分。

  • 单元测试是以测试为核心开发策略的重要组成部分。

last sentence

  • Unit testing is not a replacement for higher-level functional or system testing, but it is important in all phases of development:

  • 单元测试不能取代更高层面的有效性和系统测试,但在开发的每个阶段都很重要。

  • 单元测试不能取代更高层面的功能和系统测试,但在开发的每个阶段都很重要。

2nd item in the list

2nd sentence

  • When all the test cases pass, the function is complete.

  • 通过了测试程序就开发完成了。

  • 通过了所有测试用例程序就完全(实现)了。

3rd item in the list

last sentence between parentheses

  • “But sir, all the unit tests passed when I checked it in...”

  • 但是 先生, 我检查过,所有的单元测试都通过了......”

  • “但是,先生, 我检入(check in)代码时所有的单元测试都通过了......”

last item in the list

  • When writing code in a team, it increases confidence that the code you're about to commit isn't going to break other peoples' code, because you can run their unittests first. (I've seen this sort of thing in code sprints. A team breaks up the assignment, everybody takes the specs for their task, writes unit tests for it, then shares their unit tests with the rest of the team. That way, nobody goes off too far into developing code that won't play well with others.)

  • 在团队开发时,因为你可以事先运行他人的单元测试代码,所以可以使你有信心自己的代码不至于另他人的代码崩溃。(我曾经见到过, 一个团队共同分担的工作中,每个人都根据自己部分的需求开发单元测试,然后大家共同应用这些单元测试。 这样一来,没有人会出太大的偏差而导致不融于团队。)

  • 在团队开发时,可以使你有信心,保证自己提交的代码不会破坏其他人的代码,因为你可以先运行其他人的单元测试代码。(我在 “代码风暴”中见过这种事情。一个团队将任务拆分,每个人都根据自己那部分的需求开发单元测试,然后与其他成员共享。没有人会出太大的偏差而导致代码无法集成。)

1.3. 第13章第3节 介绍 romantest.py

http://www.woodpecker.org.cn/obp/diveintopython-zh-5.4/zh-cn/dist/htmlflat/diveintopython.html#roman.romantest

1st sentense

  • This is the complete test suite for your Roman numeral conversion functions, which are yet to be written but will eventually be in roman.py.

  • 这是将被开发并存在 roman.py 中的罗马数字转换程序的完整测试组建。

  • 这是将被开发并保存为 roman.py 的罗马数字转换程序的完整测试组件(test suite)。

Further reading

2nd item

  • The PyUnit FAQ explains why test cases are stored separately from the code they test.

  • PyUnit FAQ 解释了 如何另外存储测试 以便与被测试代码分离。

  • PyUnit FAQ 解释了为什么测试用例要和被测试代码分开存放。

1.4. 第13章第4节 成功测试法

http://www.woodpecker.org.cn/obp/diveintopython-zh-5.4/zh-cn/dist/htmlflat/diveintopython.html#roman.success

所有的test case(s)都应该翻译成“测试用例”,而不是“独立测试”

1st paragraph

  • The most fundamental part of unit testing is constructing individual test cases. A test case answers a single question about the code it is testing.

  • 单元测试的基础是构建独立测试(test case)。 一个独立测试回答一个关于被测试代码的问题。

  • 单元测试的基础是构建独立的测试用例(test case)。一个测试用例只回答一个关于被测试代码的问题。

2nd paragraph

  • A test case should be able to...

  • 一个独立测试需要:

  • 一个测试用例应该做到:

2nd item in the list

  • ...determine by itself whether the function it is testing has passed or failed, without a human interpreting the results.

  • 可以自己决断,不论被测试函数通过还是失败都不需要人工干预结果。

  • 可以自己判断被测试函数是通过还是失败,不需要人工干预结果。

下面的注解1:

  • 1 To write a test case, first subclass the TestCase class of the unittest module. This class provides many useful methods which you can use in your test case to test specific conditions.

  • 在编写独立测试时,第一个子类派生自 unittest 模块的 TestCase 类,这个类提供了用于独立测试特定情况测试的很多有用方法。

  • 编写测试用例首先需要写一个类来继承 unittest 模块中的 TestCase 类,这个类 (TestCase )提供了很多可以用在你的测试用例中来测试特定情况的有用方法。

注解2:

  • 2 This is a list of integer/numeral pairs that I verified manually. It includes the lowest ten numbers, the highest number, every number that translates to a single-character Roman numeral, and a random sampling of other valid numbers. The point of a unit test is not to test every possible input, but to test a representative sample.

  • 此时我手工转换的一个 integer/numeral 对列表。 它包含了最小的十个数,最大数,每个罗马数字单字符对应的数,以及其他随即挑选的有效数样本。单元测试的关键不在于所有可能的输入,而是一个有代表性的样本。

  • 这是我手工验证的 integer/numeral 对照列表。它包含了最小的十个数,最大数,每个单字符罗马数字对应的数,以及随机挑选其他的有效数字样本。

注解3:

  • 3 Every individual test is its own method, which must take no parameters and return no value. If the method exits normally without raising an exception, the test is considered passed; if the method raises an exception, the test is considered failed.

  • 每个独立测试都是其自己的方法,必须不需要输入值也不返回任何值。如果该方法正常退出而没有引发异常,测试被认为通过;如果测试引发异常,测试被认为失败。

  • 每个独立测试都是其自己的方法,既不需要参数也不返回任何值。如果该方法正常退出没有引发异常,测试被认为通过;如果测试引发异常,测试被认为失败。

注解4:

  • 4 Here you call the actual toRoman function. (Well, the function hasn't be written yet, but once it is, this is the line that will call it.) Notice that you have now defined the API for the toRoman function: it must take an integer (the number to convert) and return a string (the Roman numeral representation). If the API is different than that, this test is considered failed.

  • 这里你真正调用 toRoman 函数。(当然,函数还没有编写,但一旦被编写,这里便是调用之处) 注意你在这里为 toRoman 函数定义了 API :它必须接受整数(待转换的数)并返回一个字符串(罗马数字表示), 如果 API 不是这样,测试将失败。

  • 这里你真正调用了 toRoman 函数。(当然,函数还没有编写,但一旦被编写,这里便是调用之处) 注意你在这里为 toRoman 函数定义了 API :它必须接受整数(待转换的数)并返回一个字符串(对应的罗马数字表示), 如果 API 不是这样,测试将失败。

注解5:

  • 5 Also notice that you are not trapping any exceptions when you call toRoman. This is intentional. toRoman shouldn't raise an exception when you call it with valid input, and these input values are all valid. If toRoman raises an exception, this test is considered failed.

  • 同样值得注意,你在调用 toRoman 时没有使用任何可能发生意外的数。这正是我们所希望的,以有效输入调用,不会引发任何异常,这里的输入都是有效的。如果 toRoman 引发了异常,则测试被认为失败。

  • 同样值得注意,你在调用 toRoman 时没有试图捕捉任何可能发生的异常。这正是我们所希望的。以有效输入调用toRoman,不会引发任何异常,因此这些输入都是有效的。如果 toRoman 引发了异常,则测试被认为失败(输入是无效的)。

注解6:

  • 6 Assuming the toRoman function was defined correctly, called correctly, completed successfully, and returned a value, the last step is to check whether it returned the right value. This is a common question, and the TestCase class provides a method, assertEqual, to check whether two values are equal. If the result returned from toRoman (result) does not match the known value you were expecting (numeral), assertEqual will raise an exception and the test will fail. If the two values are equal, assertEqual will do nothing. If every value returned from toRoman matches the known value you expect, assertEqual never raises an exception, so testToRomanKnownValues eventually exits normally, which means toRoman has passed this test.

  • 假设 toRoman 函数被正确编写,正确调用,完全成功并返回一个值,最后一步便是这个返回值正确与否。这是一个常见的问题, TestCase 类提供了一个方法: assertEqual 来测试两个值是否相等。如果 toRoman 返回的 (value) 不像我们预期的等于 (numeral), assertEqual 将会引发一个异常,测试也就此失败。如果两个值相等,assertEqual 什么也不做。如果每个从 toRoman 返回的值都等于预期值 assertEqual 便不会引发异常, testToRomanKnownValues 最终正常退出,这意味着 toRoman 通过了该测试。

  • 假设 toRoman 函数被正确编写,正确调用,运行成功并返回一个值,最后一步便是检查这个返回值正确与否。这是一个常见的问题, TestCase 类提供了一个方法assertEqual 来测试两个值是否相等。如果 toRoman 返回的结果 (result) 不等于我们预期的值 (numeral), assertEqual 将会引发一个异常,测试也就此失败。如果两个值相等,assertEqual 什么也不做。如果每个从 toRoman 返回的值都等于预期值, assertEqual 就决不会引发异常, 于是testToRomanKnownValues 最终正常退出,这就意味着 toRoman 通过了该测试。

1.5. 第13章第5节 失败测试法

http://www.woodpecker.org.cn/obp/diveintopython-zh-5.4/zh-cn/dist/htmlflat/diveintopython.html#roman.failure

注解2:

  • 2 Along with testing numbers that are too large, you need to test numbers that are too small. Remember, Roman numerals cannot express 0 or negative numbers, so you have a test case for each of those (testZero and testNegative).

  • 与测试过大的数相伴的便是测试过小的数。记住,罗马数字不能表示 0 和负数,所以你要分别编写独立测试( testZero 和 testNegative)。

  • 与测试过大的数相伴的便是测试过小的数。记住,罗马数字不能表示 0 和负数,所以你要分别编写测试用例( testZero 和 testNegative)。

1.6. 第13章第6节 回旋测试(Testing for sanity)

http://www.woodpecker.org.cn/obp/diveintopython-zh-5.4/zh-cn/dist/htmlflat/diveintopython.html#roman.sanity

1st paragraph

  • Often, you will find that a unit of code contains a set of reciprocal functions, usually in the form of conversion functions where one converts A to B and the other converts B to A. In these cases, it is useful to create a “sanity check” to make sure that you can convert A to B and back to A without losing precision, incurring rounding errors, or triggering any other sort of bug.

  • 你经常会发现一组代码中包含互反函数,他们通常是转换函数,一个把 A 转换为 B ,另一个把 B 转换为 A。 在这种情况下,创建“回旋检查”可以使你在由 A 转 B 再转 A 的过程中确保精确,不至于出现取整等错误。

  • 你经常会发现一组代码中包含互逆函数,它们通常是转换函数,一个把 A 转换为 B ,另一个把 B 转换为 A。 在这种情况下,创建“回旋检查”可以使你确保在由 A 转 B 再转回 A 的过程中不会出现丢失精度和取整等错误。

3rd paragraph

  • you should end up with the number you started with.

  • 你所得到的应该是最初改定的那个数。

  • 你所得到的应该是最初给定的那个数。

The line just before requirements 7 & 8

  • The last two requirements are different from the others because they seem both arbitrary and trivial:

  • 最后两个要求 和其他的要求不同,似乎即武断又琐碎:

  • 最后两个要求 和其他的要求不同,似乎即霸道又没有什么价值:

paragraph before ex. 13.6

  • In fact, they are somewhat arbitrary.

  • 事实上,它们确实有点武断

  • 事实上,它们确实有点霸道。

  • But they are not completely arbitrary;

  • 但他们也不是完全武断;

  • 但他们也不是完全霸道;

  • The fact that it only accepts uppercase input is arbitrary, but as any systems integrator will tell you, case always matters, so it's worth specifying the behavior up front. And if it's worth specifying, it's worth testing.

  • 但归根结底,只接受大写输入还是武断, 但就像每个系统都回告诉你的那样,大小写总是个问题,因此事先规定这一点还是有必要的。既然是有必要规定的,那么也有必要测试。

  • 不过怎么说只接受大写输入还是霸道, 但就像每个系统集成者都会告诉你的那样,大小写总会出问题,因此事先规定这一点还是有必要的。既然是有必要的规定,那么也就有必要测试。

    ex. 13.6 注解1

  • 1 The most interesting thing about this test case is all the things it doesn't test. It doesn't test that the value returned from toRoman is right or even consistent; those questions are answered by separate test cases. You have a whole test case just to test for uppercase-ness. You might be tempted to combine this with the sanity check, since both run through the entire range of values and call toRoman.[6] But that would violate one of the fundamental rules: each test case should answer only a single question. Imagine that you combined this case check with the sanity check, and then that test case failed. You would need to do further analysis to figure out which part of the test case failed to determine what the problem was. If you need to analyze the results of your unit testing just to figure out what they mean, it's a sure sign that you've mis-designed your test cases.

  • 很有意思的是这个独立测试它并不测试什么。它所测试的不是 toRoman 的返回值是否 正确 或者 一致;这些问题由其他独立测试来回答。整个这个独立测试仅仅是测试大写问题。你也许觉得应该将它并入到 回旋检查,毕竟都要遍历整个输入值范围并调用 toRoman。[6] 但是这样将会违背一条 基本规则:每个独立测试都应该只回答单一的问题。试想一下,你将这个测试并入到回旋检查中并遇到了测试失败。 你还需要进一步分析以便判定独立测试的哪部分出了问题。 如果你需要分析方能找出问题所在,无疑你的独立测试在设计上出了问题。

  • 很有意思的是这个测试用例它并不测试什么。它所测试的不是 toRoman 的返回值是否 正确 或者 一致;这些问题由其他测试用例来回答。整个这个测试用例仅仅是测试大写问题。你也许觉得应该将它并入到 回旋检查,因为都要遍历整个输入值范围并调用 toRoman。[6] 但是这样将会违背一条 基本规则:每个测试用例都应该只回答单一的问题。试想一下,你将这个测试用例并入到回旋检查中并遇到了测试失败。 你将需要进一步分析以便判定是测试用例的哪部分出了问题。 如果你需要分析才能找出问题所在,那么你的测试用例肯定在设计上出了问题。

    ex. 13.6 注解1

  • 2 There's a similar lesson to be learned here: even though “you know” that toRoman always returns uppercase, you are explicitly converting its return value to uppercase here to test that fromRoman accepts uppercase input. Why? Because the fact that toRoman always returns uppercase is an independent requirement. If you changed that requirement so that, for instance, it always returned lowercase, the testToRomanCase test case would need to change, but this test case would still work. This was another of the fundamental rules: each test case must be able to work in isolation from any of the others. Every test case is an island.

  • 这有一个和前面相似的情况: 尽管 “你知道” toRoman 总是返回大写字母,你可以直接把返回值传递给只接受大写的 fromRoman 进行测试。 为什么?因为 toRoman 只返回大写字母是一个独立的要求。如果你改变了这个要求,例如改成总是返回小写字母,那么 testToRomanCase 独立测试也应作出调整,独立测试应该仍能通过。 这是另外一个 基本规则:每个独立测试必须可以与其他独立测试隔离, 每个独立测试是一个孤岛。

  • 这有一个和前面相似的情况: 尽管 “你知道” toRoman 总是返回大写字母,你还是需要把返回值显式地转换成大写字母后再传递给只接受大写的 fromRoman 进行测试。 为什么?因为 toRoman 只返回大写字母是一个独立的需求。如果你改变了这个需求,例如改成总是返回小写字母,那么 testToRomanCase 测试用例也应作出调整,但这个测试用例应该仍能通过。 这是另外一个 基本规则:每个测试用例必须可以与其它测试用例隔离地工作,每个测试用例都是一个“孤岛”。

    ex. 13.6 注解1

  • 3 Note that you're not assigning the return value of fromRoman to anything. This is legal syntax in Python; if a function returns a value but nobody's listening, Python just throws away the return value. In this case, that's what you want. This test case doesn't test anything about the return value; it just tests that fromRoman accepts the uppercase input without raising an exception.

  • 注意你并没有使用 fromRoman 的返回值。 这是一个合法的 Python 语法:如果一个函数返回一个值,但没有被使用, Python 会直接把这个返回值扔掉。 这正是你所希望的,这个独立测试并不对返回值进行测试,只是测试 fromRoman 接受大写字母而不引发异常。

  • 注意你并没有使用 fromRoman 的返回值。 这是一个合法的 Python 语法:如果一个函数返回一个值,但没有被使用, Python 会直接把这个返回值扔掉。 这正是你所希望的,这个测试用例并不对返回值进行测试,只是测试 fromRoman 接受大写字母而不引发异常。

    ex. 13.6 注解1

  • 4 This is a complicated line, but it's very similar to what you did in the ToRomanBadInput and FromRomanBadInput tests. You are testing to make sure that calling a particular function (roman.fromRoman) with a particular value (numeral.lower(), the lowercase version of the current Roman numeral in the loop) raises a particular exception (roman.InvalidRomanNumeralError). If it does (each time through the loop), the test passes; if even one time it does something else (like raises a different exception, or returning a value without raising an exception at all), the test fails.

  • 这行有点复杂,但是它与 ToRomanBadInputFromRomanBadInput 测试很相似。 你在测试以确保特定函数 (roman.fromRoman)接受特定值(numeral.lower(),循环中目前罗马数字的小写版)引发特定的异常 (roman.InvalidRomanNumeralError)。 如果(在循环中的每一次)确实如此,测试通过;如果有一次不是这样(比如引发另外的异常或者不引发异常),测试失败。

  • 这行有点复杂,但是它与 ToRomanBadInputFromRomanBadInput 测试很相似。 你在测试特定函数 (roman.fromRoman)接受特定值(numeral.lower(),循环中目前罗马数字的小写版)会确实引发特定的异常 (roman.InvalidRomanNumeralError)。 如果(在循环中的每一次)确实如此,测试通过;如果有一次不是这样(比如引发另外的异常或者不引发异常),测试失败。

last paragraph

  • In the next chapter, you'll see how to write code that passes these tests.

  • 在下一章中,你将看到如何编写可以通过测试的代码。

  • 在下一章中,你将看到如何编写可以通过这些测试的代码。