html tool

2013年7月24日星期三

Dive into Python 的我的翻译 XLII------14.1 II

Example 14.1. roman1.py

  •  This file is available in py/roman/stage1/ in the examples directory.
    If you have not already done so, you can download this and other examples (http://diveintopython.org/download/diveintopython−examples−5.4.zip) used in this book.
    [pope译]
    这个可用的文件在py/roman/stage1/ 例子的文件夹中。
    如果你还没有,可以在http://diveintopython.org/download/diveintopython−examples−5.4.zip 中下载使用本书的其他例子
    [net 译 来源:http://woodpecker.org.cn/diveintopython/unit_testing/stage_1.html ]
    这个程序可以在例子目录下的 py/roman/stage1/ 目录中找到。
    如果您还没有下载本书附带的样例程序, 可以 下载本程序和其他样例程序


    """Convert to and from Roman numerals"""
    #Define exceptions
    class RomanError(Exception): pass--------------------------------------------------------------1               
    class OutOfRangeError(RomanError): pass--------------------------------------------------------------2         
    class NotIntegerError(RomanError): pass
    class InvalidRomanNumeralError(RomanError): pass -------------------------------------------------3
    def toRoman(n):
        """convert integer to Roman numeral"""--------------------------------------------------------------4
        pass                                        
    def fromRoman(s):
        """convert Roman numeral to integer"""
        pass
    • 1[原文]P198
      This is how you define your own custom exceptions in Python. Exceptions are classes, and you create your own by subclassing existing exceptions. It is strongly recommended (but not required) that you subclass Exception, which is the base class that all built−in exceptions inherit from. Here I am defining RomanError (inherited from Exception) to act as the base class for all my other custom exceptions to follow. This is a matter of style; I could just as easily have inherited each individual exception from the Exception class directly
      [pope译]
      这是你在python中如何定义自己的异常。异常是类,你通过继承存在的异常类创建你自己的异常类。创建Exception 的子集被强烈推荐,但不强制,这个是全部嵌入的异常通过继承得到的。这里我定义的RomanError(继承Exception)作为我当前异常的基类使用。这是个风格问题,我也可以每个异常类只直接简单的继承Exception。
      [popexizhi:这个继承的风格,自己第一次看时还真没有好好注意:)]
      [net 译]
      这就是如何定义你自己的 Python 异常。异常 (Exception) 也是类,通过继承已有的异常,你可以创建自定义的异常。强烈建议 (但不是必须) 你继承 Exception 来定义自己的异常,因为它是所有内建异常的基类。这里我定义了 RomanError (从 Exception 继承而来) 作为我所有自定义异常的基类。这是一个风格问题,我也可以直接从 Exception继承建立每一个自定义异常。
      [popexizhi]
      which is the base class that all built−in exceptions inherit from
      [pope译]这个是全部嵌入的异常通过继承得到的
      [net 译]因为它是所有内建异常的基类
      [popexizhi] 内建异常的基类


      2[原文]P198
      The OutOfRangeError and NotIntegerError exceptions will eventually be used by toRoman to flag various forms of invalid input, as specified in ToRomanBadInput.
      [pope译]
      OutOfRangeError 和 NotIntegerError 异常是toRoman用来标志不同形式的错误输入使用的,在ToRomanBadInput中特别指出的
      [net 译]
      OutOfRangeError  NotIntegerError 异常将会最终被用于 toRoman 以标示不同类型的无效输入,更具体而言就是 ToRomanBadInput 测试的那些。

      3[原文]P198
      The InvalidRomanNumeralError exception will eventually be used by fromRoman to flag invalid input, as specified in FromRomanBadInput.
      [pope译]
      InvalidRomanNumeralError异常是fromRoman用来标记错误输入使用的,在FromRomanBadInput中特别测试对应内容
      [net 译]
      InvalidRomanNumeralError 将被最终用于 fromRoman 以标示无效输入,具体而言就是 FromRomanBadInput测试的那些。
      4[原文]P198
      At this stage, you want to define the API of each of your functions, but you don't want to code them yet, so you stub them out using the Python reserved word pass.
      [pope译]
      在这个过程中,你想要定义实现功能的API,但你现在不打算使用代码实现,所以你使用python保留的关键字pass 充填这个过程。
      [net 译]
      在这一步中你只是想定义每个函数的 API ,而不想具体实现它们,因此你以 Python 关键字 pass 姑且带过。
    [原文]P198
    Now for the big moment (drum roll please): you're finally going to run the unit test against this stubby little module. At this point, every test case should fail. In fact, if any test case passes in stage 1, you should go back to romantest.py and re−evaluate why you coded a test so useless that it passes with do−nothing functions.
    [pope译]
    现在有一个重要信息(打起精神来注意了):你之后要针对这个简短的模块运行单元测试。在当前点,每个单元测试都应该失败。如果有什么单元测试在stage1中成功,你到应该返回到romantest.py 中重新评估一下{re-evaluate}为什么你的单元测试代码这么没有,竟然让什么都没做的功能通过测试了。
    [net 译]
    重要的时刻到了 (请打起鼓来):你终于要对这个简陋的小模块开始运行单元测试了。目前而言,每一个测试用例都应该失败。事实上,任何测试用例在此时通过,你都应该回头看看 romantest.py ,仔细想想为什么你写的测试代码如此没用,以至于连什么都不作的函数都能通过测试。
    [popexizhi]
    at this point
    [pope译]在当前点
    [net 译]目前而言
    [popexizhi] at this point 目前而言,学习了
    [原文]P198
    Run romantest1.py with the −v command−line option, which will give more verbose output so you can see exactly what's going on as each test case runs. With any luck, your output should look like this:

    [pope译]
    使用-v命令行参数运行romantest1.py,它将给出更多的详细{verbose}输出这样你就可以看到每个测试用例运行的精确的过程。如果一切幸运的话{with any luck},你看到的输出类似如下:
    [net 译]
    用命令行选项 -v 运行 romantest1.py 可以得到更详细的输出信息,这样你就可以看到每一个测试用例的具体运行情况。如果幸运,你的结果应该是这样的
    + - Example 14.2. Output of romantest1.py against roman1.py
    • fromRoman should only accept uppercase input ... ERROR
      toRoman should always return uppercase ... ERROR
      fromRoman should fail with malformed antecedents ... FAIL
      fromRoman should fail with repeated pairs of numerals ... FAIL
      fromRoman should fail with too many repeated numerals ... FAIL
      fromRoman should give known result with known input ... FAIL
      toRoman should give known result with known input ... FAIL
      fromRoman(toRoman(n))==n for all n ... FAIL
      toRoman should fail with non−integer input ... FAIL
      toRoman should fail with negative input ... FAIL
      toRoman should fail with large input ... FAIL
      toRoman should fail with 0 input ... FAIL
      ======================================================================
      ERROR: fromRoman should only accept uppercase input
      −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
      Traceback (most recent call last):
        File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 154, in testFromRomanCase
          roman1.fromRoman(numeral.upper())
      AttributeError: 'None' object has no attribute 'upper'
      ======================================================================
      ERROR: toRoman should always return uppercase
      −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
      Traceback (most recent call last):
        File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 148, in testToRomanCase
          self.assertEqual(numeral, numeral.upper())
      AttributeError: 'None' object has no attribute 'upper'
      ======================================================================
      FAIL: fromRoman should fail with malformed antecedents
      −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
      Traceback (most recent call last):
        File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 133, in testMalformedAntecedent
          self.assertRaises(roman1.InvalidRomanNumeralError, roman1.fromRoman, s)
        File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
          raise self.failureException, excName
      AssertionError: InvalidRomanNumeralError
      ======================================================================
      FAIL: fromRoman should fail with repeated pairs of numerals
      −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
      Traceback (most recent call last):
        File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 127, in testRepeatedPairs
          self.assertRaises(roman1.InvalidRomanNumeralError, roman1.fromRoman, s)
        File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
          raise self.failureException, excName
      AssertionError: InvalidRomanNumeralError
      ======================================================================
      FAIL: fromRoman should fail with too many repeated numerals
      −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
      Traceback (most recent call last):
        File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 122, in testTooManyRepeatedNumerals
          self.assertRaises(roman1.InvalidRomanNumeralError, roman1.fromRoman, s)
        File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
          raise self.failureException, excName
      AssertionError: InvalidRomanNumeralError
      ======================================================================
      FAIL: fromRoman should give known result with known input
      −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
      Traceback (most recent call last):
        File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 99, in testFromRomanKnownValues
          self.assertEqual(integer, result)
        File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual
          raise self.failureException, (msg or '%s != %s' % (first, second))
      AssertionError: 1 != None
      ======================================================================
      FAIL: toRoman should give known result with known input
      −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
      Traceback (most recent call last):
        File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 93, in testToRomanKnownValues
          self.assertEqual(numeral, result)
        File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual
          raise self.failureException, (msg or '%s != %s' % (first, second))
      AssertionError: I != None
      ======================================================================
      FAIL: fromRoman(toRoman(n))==n for all n
      −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
      Traceback (most recent call last):
        File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 141, in testSanity
          self.assertEqual(integer, result)
        File "c:\python21\lib\unittest.py", line 273, in failUnlessEqual
          raise self.failureException, (msg or '%s != %s' % (first, second))
      AssertionError: 1 != None
      ======================================================================
      FAIL: toRoman should fail with non−integer input
      −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
      Traceback (most recent call last):
        File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 116, in testNonInteger
          self.assertRaises(roman1.NotIntegerError, roman1.toRoman, 0.5)
        File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
          raise self.failureException, excName
      AssertionError: NotIntegerError
      ======================================================================
      FAIL: toRoman should fail with negative input
      −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
      Traceback (most recent call last):
        File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 112, in testNegative
          self.assertRaises(roman1.OutOfRangeError, roman1.toRoman, −1)
        File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
          raise self.failureException, excName
      AssertionError: OutOfRangeError
      ======================================================================
      FAIL: toRoman should fail with large input
      −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
      Traceback (most recent call last):
        File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 104, in testTooLarge
          self.assertRaises(roman1.OutOfRangeError, roman1.toRoman, 4000)
        File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
          raise self.failureException, excName
      AssertionError: OutOfRangeError
      ======================================================================
      FAIL: toRoman should fail with 0 input                           ----------------------------------------------------------------------------------------------------------------------------1     
      −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
      Traceback (most recent call last):
        File "C:\docbook\dip\py\roman\stage1\romantest1.py", line 108, in testZero
          self.assertRaises(roman1.OutOfRangeError, roman1.toRoman, 0)
        File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises
          raise self.failureException, excName
      AssertionError: OutOfRangeError                                       ----------------------------------------------------------------------------------------------------------------------------2
      −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
      Ran 12 tests in 0.040s                                                      ----------------------------------------------------------------------------------------------------------------------------3
      FAILED (failures=10, errors=2)                                         ----------------------------------------------------------------------------------------------------------------------------4
      • 1[原文]P200
        Running the script runs unittest.main(), which runs each test case, which is to say each method defined in each class within romantest.py. For each test case, it prints out the doc string of the method and whether that test passed or failed. As expected, none of the test cases passed.
        [pope ]
        运行脚本时运行unittest.main()将运行每一个测试用例,也就是说romantest.py中的每个类的定义的每个方法。每个测试用例都将打印出此方法的doc string,与是否测试通过无关。就像预期一样,没有通过测试的测试用例。

        [pope译]
        运行脚本时unittest.main(),运行每一个测试用例,也就是说在romantest.py中的每个类中定义的每个方法都被运行。每个测试用例打印出来方法的文档字符串和是否测试通过。和预期的一样{as expected},没有测试用例通过测试。
        [net 译 来源:http://woodpecker.org.cn/diveintopython/unit_testing/stage_1.html]
        运行脚本将会执行 unittest.main(),由它来执行每个测试用例,也就是每个在 romantest.py 中定义的方法。对于每个测试用例,无论测试通过与否,都会输出这个方法的 doc string。意料之中,没有通过一个测试用例。
        [popexizhi:]
        each method defined in each class within romantest.py
        [pope译]在romantest.py中的每个类中定义的每个方法
        [net 译]每个在 romantest.py 中定义的方法
        [popexizhi]net翻译中的each class 被net省略了,也对吧,反正都在.py中吧
        it prints out the doc string of the method and whether that test passed or failed.
        [pope译]每个测试用例打印出来方法的文档字符串和是否测试通过。
        [net 译]对于每个测试用例,无论测试通过与否,都会输出这个方法的 doc string
        [popexizhi] 这里的and是表示条件的,这里pope翻译中没有理解打印出的doc string 是方法默认的文字说明。


        2[原文]P201
        For each failed test case, unittest displays the trace information showing exactly what happened. In this case, the call to assertRaises (also called failUnlessRaises) raised an AssertionError because it was expecting toRoman to raise an OutOfRangeError and it didn't

        [pope译]
        对每个测试失败的用例,单元测试展示跟踪{trace}信息精确的{exactly}显示发生了什么.在这个例子中,调用assertRaises(也叫做failUniessRaises)抛出{raised}AssertionError 因为它预期 toRoman 应该抛出OutOfRangeError 异常但它没有抛出。
        [net 译]
        对于每个失败的测试用例,unittest 显示的跟踪信息告诉我们都发生了什么。就此处而言,调用 assertRaises (也称作 failUnlessRaises) 引发了一个 AssertionError 异常,因为期待 toRoman 所引发的 OutOfRangeError 异常没有出现
        3[原文]P201
        After the detail, unittest displays a summary of how many tests were performed and how long it took.
        [pope译]
        在这些细节之后,单元测试展示有多少测试执行{performed}和它运行多久的总结。
        [net译]
        在这些细节后面,unittest 给出了一个关于被执行测试的个数和花费时间的总结。
        4[原文]P201
        Overall, the unit test failed because at least one test case did not pass. When a test case doesn't pass, unittest distinguishes between failures and errors. A failure is a call to an assertXYZ method, like assertEqual or assertRaises, that fails because the asserted condition is not true or the expected exception was not raised. An error is any other sort of exception raised in the code you're testing or the unit test case itself. For instance, the testFromRomanCase method ("fromRoman should only accept uppercase input") was an error, because the call to numeral.upper() raised an AttributeError exception, because toRoman was supposed to return a string but didn't. But testZero ("toRoman should fail with 0 input") was a failure, because the call to fromRoman did not raise the InvalidRomanNumeral exception that assertRaises was looking for.
        [pope译]
        总计一下,单元测试失败是因为至少有一个测试用例没有通过测试。当测试用例不能通过时,单元测试区分了{distinguishes} 失败和错误。失败是调用assertXYZ 方法,像assertEqual或者assertRaises,失败是因为声明的条件{asserted condition}不为真或者预期的异常{the expected exception }没有抛出。错误是在你的测试代码或者单元测试用例自身中各种种类的异常抛出。例如:testFromRomanCase 方法的("fromRoman should only accept uppercase input") 就是一个错误,因为调用 numeral.upper() 抛出一个 AttributeError 异常,因为 toRoman 应该(was supposed to)返回字符串却没有. 但testZero ("toRoman should fail with 0 input")是一个失败,因为调用fromRoman 没有抛出assertRaises查找的 InvalidRomanNumeral 异常。
        [net译 来源:http://woodpecker.org.cn/diveintopython/unit_testing/stage_1.html ]
        总而言之,由于至少一个测试用例没有通过,单元测试失败了。当某个测试用例没能通过时,unittest 会区分是失败 (failures) 还是错误 (errors)。失败是指调用 assertXYZ方法,比如 assertEqual 或者 assertRaises 时,断言的情况没有发生或预期的异常没有被引发。而错误是指你测试的代码或单元测试本身发生了某种异常。例如:testFromRomanCase 方法 (“fromRoman 只接受大写输入”) 就是一个错误,因为调用 numeral.upper() 引发了一个 AttributeError 异常,因为 toRoman 的返回值不是期望的字符串类型。但是,testZero (“toRoman 应该在输入 0 时失败”) 是一个失败,因为调用 fromRoman 没有引发一个 assertRaises 期待的异常:InvalidRomanNumeral
        [popexizhi]
        because the call to numeral.upper() raised an AttributeError exception,because toRoman was supposed to return a string but didn't.
        [pope译]因为 toRoman 应该(was supposed to)返回字符串却没有.
        [net 译]因为 toRoman 的返回值不是期望的字符串类型
        [popexizhi]这个位置翻译的没有太多问题,查了一下源码,是因为这里的unittest原文如下:

        def testFromRomanCase(self):
                """fromRoman should only accept uppercase input"""
                for integer in range(1, 4000):
                    numeral = roman1.toRoman(integer)
                    roman1.fromRoman(numeral.upper())
                    self.assertRaises(roman1.InvalidRomanNumeralError,
                                      roman1.fromRoman, numeral.lower())

        这里的错误是numeral.upper() 但原因是numeral = roman1.toRoman(integer)返回pass引起的,这样就很明白了,看了翻译时,还是上下文很重要啊:)


        [popexizhi]失败是断言期望的失败,而错误是代码本身的错误(但有可能是被测试代码引起的啊:)),所以例子中才会提到testZero没有得到预期的异常    

没有评论:

发表评论