【问题标题】:Unit testing using unit test in Python3在 Python3 中使用单元测试进行单元测试
【发布时间】:2019-02-10 11:45:06
【问题描述】:

我需要使用 Python 中的单元测试编写测试用例来测试圈子的创建。

  • 使用方法__init__ 定义一个类Circle,该类使用属性radius 初始化一个圆,具有以下限制:

    • radius 必须是数值,如果不引发类型错误,错误消息“半径必须是数字”。

    • radius 两边必须介于 0 到 1000 之间,否则会引发错误消息“半径必须介于 0 到 1000 之间”

    • 定义一个类方法areacircumference,它们必须返回四舍五入到小数点后两位的值。

    完成类TestingCircleCreation的定义,该类测试__init__方法的行为,如下所述。

  • 定义test_creating_circle_with_numerical_radius的测试方法,创建半径为2.5的圆并检查半径是否与值2.5匹配

  • 定义测试方法test_creating_circle_with_negative_radius,在创建半径为 2.5 的圆时检查是否引发值错误异常并显示错误消息“半径必须介于 0 和 1000 之间”。

  • 定义测试方法test_creating_circle_with_greaterthan_radius 检查是否引发ValueError 异常并显示错误消息“半径必须介于 0 和 1000 之间”,同时创建半径为 1000.1 的圆。

  • 定义测试方法test_creating_circle_with_nonnumeric_radius,它检查TypeError 在创建半径为“hello”的圆时是否引发异常并显示错误消息“radius must be number”。

我在下面尝试过,但失败并出现错误

Traceback (most recent call last):
  File "..\Playground\", line 86, in <module>
    pass_count = pass_count[0]
IndexError: list index out of range

代码:

import inspect
import re
import unittest
import math

# Define below the class 'Circle' and it's methods with proper doctests.
class Circle:
    def __init__(self, radius):
        # Define the initialization method below
        try:
            if not isinstance(radius, (int, float)):
                raise TypeError 
            elif 1000 >=radius>=0:
                    self.radius=radius 
            else:
                raise ValueError        
        except ValueError:
            raise ValueError("radius must be between 0 and 1000 inclusive")
        except TypeError:
            raise TypeError("radius must be a number")
        
    def area(self):
        # Define the area functionality below
        y=math.pi*(self.radius**2)
        return round(y,2)
               
    def circumference(self):
        # Define the circumference functionality below
        x=math.pi*2*self.radius
        return round(x,2)
        
class TestCircleCreation(unittest.TestCase):
    def test_creating_circle_with_numeric_radius(self):
        # Define a circle 'c1' with radius 2.5 and check if 
        # the value of c1.radius equal to 2.5 or not
        c1=Circle(2.5)        
        self.assertEqual(c1.radius,2.5)

    def test_creating_circle_with_negative_radius(self):
        # Try Defining a circle 'c' with radius -2.5 and see 
        # if it raises a ValueError with the message
        # "radius must be between 0 and 1000 inclusive"
        c=Circle(-2.5)
        self.assertEqual(c.radius,-2.5)
        self.assertRaises(ValueError)      

    def test_creating_circle_with_greaterthan_radius(self):
        # Try Defining a circle 'c' with radius 1000.1 and see 
        # if it raises a ValueError with the message
        # "radius must be between 0 and 1000 inclusive"
        c=Circle(1000.1)        
        self.assertEqual(c.radius,1000.1)
        self.assertRaises(ValueError)        
        
    def test_creating_circle_with_nonnumeric_radius(self):
        # Try Defining a circle 'c' with radius 'hello' and see 
        # if it raises a TypeError with the message
        # "radius must be a number"
        c=Circle('hello')      
        self.assertEqual(c.radius,'hello')
        self.assertRaises(TypeError)        

if __name__ == '__main__':
    fptr = open('output.txt', 'w')
    
    runner = unittest.TextTestRunner(fptr)
    
    unittest.main(testRunner=runner, exit=False)
    
    fptr.close()
    
    with open('output.txt') as fp:
        output_lines = fp.readlines()
    
    pass_count = [ len(re.findall(r'\.', line)) for line in output_lines if line.startswith('.')
                     and line.endswith('.\n')]
    
    pass_count = pass_count[0]
    print(str(pass_count))
                       
    doc1 = inspect.getsource(TestCircleCreation.test_creating_circle_with_numeric_radius)
    doc2 = inspect.getsource(TestCircleCreation.test_creating_circle_with_negative_radius)
    doc3 = inspect.getsource(TestCircleCreation.test_creating_circle_with_greaterthan_radius)
    doc4 = inspect.getsource(TestCircleCreation.test_creating_circle_with_nonnumeric_radius)
    
    assert1_count = len(re.findall(r'assertEqual', doc1))
    print(str(assert1_count))
    
    assert1_count = len(re.findall(r'assertEqual', doc2))
    assert2_count = len(re.findall(r'assertRaises', doc2))
    print(str(assert1_count), str(assert2_count))
    
    assert1_count = len(re.findall(r'assertEqual', doc3))
    assert2_count = len(re.findall(r'assertRaises', doc3))
    print(str(assert1_count), str(assert2_count))
    
    assert1_count = len(re.findall(r'assertEqual', doc4))
    assert2_count = len(re.findall(r'assertRaises', doc4))
    print(str(assert1_count), str(assert2_count))

【问题讨论】:

    标签: python python-3.x unit-testing


    【解决方案1】:
    import inspect
    import re
    import unittest
    import math
    
    class Circle:    
        def __init__(self, radius):
            # Define the initialization method below
            self.radius=radius
            if not isinstance(self.radius,(int,float)):
                raise TypeError("radius must be a number")
            elif(self.radius>1000 or self.radius<0):
                raise ValueError("radius must be between 0 and 1000 inclusive")
            else:
                pass
        def area(self):
            # Define the area functionality below
            return math.pi*(self.radius**2)
        def circumference(self):
            return 2*math.pi*self.radius
            # Define the circumference functionality below
    
    class TestCircleCreation(unittest.TestCase):
        def test_creating_circle_with_numeric_radius(self):
            # Define a circle 'c1' with radius 2.5 and check if 
            # the value of c1.radius equal to 2.5 or not
            c1=Circle(2.5)
            self.assertEqual(c1.radius,2.5)
           
        def test_creating_circle_with_negative_radius(self):
            # Try Defining a circle 'c' with radius -2.5 and see 
            # if it raises a ValueError with the message
            # "radius must be between 0 and 1000 inclusive"
            with self.assertRaises(ValueError) as e:
                c=Circle(-2.5)
            self.assertEqual(str(e.exception),"radius must be between 0 and 1000 inclusive")
            
        def test_creating_circle_with_greaterthan_radius(self):
            # Try Defining a circle 'c' with radius 1000.1 and see 
            # if it raises a ValueError with the message
            # "radius must be between 0 and 1000 inclusive"        
            with self.assertRaises(ValueError) as e:
                c=Circle(1000.1)
            self.assertEqual(str(e.exception),"radius must be between 0 and 1000 inclusive")
           
        def test_creating_circle_with_nonnumeric_radius(self):
            # Try Defining a circle 'c' with radius 'hello' and see 
            # if it raises a TypeError with the message
            # "radius must be a number"   
            with self.assertRaises(TypeError) as e:
                c=Circle("hello")
            self.assertEqual(str(e.exception),"radius must be a number")     
    
    if __name__ == '__main__':
        fptr = open('output.txt', 'w')
        
        runner = unittest.TextTestRunner(fptr)
        
        unittest.main(testRunner=runner, exit=False)
        
        fptr.close()
        
        with open('output.txt') as fp:
            output_lines = fp.readlines()
        
        pass_count = [ len(re.findall(r'\.', line)) for line in output_lines if line.startswith('.')
                         and line.endswith('.\n')]
        
        pass_count = pass_count[0]          
        print(str(pass_count))
                           
        doc1 = inspect.getsource(TestCircleCreation.test_creating_circle_with_numeric_radius)
        doc2 = inspect.getsource(TestCircleCreation.test_creating_circle_with_negative_radius)
        doc3 = inspect.getsource(TestCircleCreation.test_creating_circle_with_greaterthan_radius)
        doc4 = inspect.getsource(TestCircleCreation.test_creating_circle_with_nonnumeric_radius)
        
        assert1_count = len(re.findall(r'assertEqual', doc1))
        print(str(assert1_count))
        
        assert1_count = len(re.findall(r'assertEqual', doc2))
        assert2_count = len(re.findall(r'assertRaises', doc2))
        print(str(assert1_count), str(assert2_count))
        
        assert1_count = len(re.findall(r'assertEqual', doc3))
        assert2_count = len(re.findall(r'assertRaises', doc3))
        print(str(assert1_count), str(assert2_count))
        
        assert1_count = len(re.findall(r'assertEqual', doc4))
        assert2_count = len(re.findall(r'assertRaises', doc4))
        print(str(assert1_count), str(assert2_count))
    

    【讨论】:

      【解决方案2】:
      class Circle:
          def __init__(self, radius):
              # Define initialization method:
              self.radius = 0
              if not isinstance(radius,(int,float)):
                  raise TypeError("radius must be a number")
              elif radius < 0 or radius > 1000:
                  raise ValueError("radius must be between 0 and 1000 inclusive")
              else:
                  self.radius = radius
              
          def area(self):
              # Define area functionality:
              return round(math.pi*(self.radius**2),2)
                     
          def circumference(self):
              # Define circumference functionality:
              return round(2*math.pi*self.radius)
      
      class TestCircleCreation(unittest.TestCase):
          def test_creating_circle_with_numeric_radius(self):
              # Define a circle 'c1' with radius 2.5, and check if 
              # the value of c1.radius is equal to 2.5 or not.
              c1 = Circle(2.5)
              self.assertEqual(c1.radius, 2.5)
      
          def test_creating_circle_with_negative_radius(self):
              # Define a circle 'c' with radius -2.5, and check 
              # if it raises a ValueError with the message
              # "radius must be between 0 and 1000 inclusive".
              with self.assertRaises(ValueError) as e:
                  c = Circle(-2.5)
              self.assertEqual(str(e.exception),"radius must be between 0 and 1000 inclusive")
           
          def test_creating_circle_with_greaterthan_radius(self):
              # Define a circle 'c' with radius 1000.1, and check 
              # if it raises a ValueError with the message
              # "radius must be between 0 and 1000 inclusive".        
              with self.assertRaises(ValueError) as e:
                  c = Circle(1000.1)
              self.assertEqual(str(e.exception),"radius must be between 0 and 1000 inclusive")      
      
          def test_creating_circle_with_nonnumeric_radius(self):
              # Define a circle 'c' with radius 'hello' and check 
              # if it raises a TypeError with the message
              # "radius must be a number".        
              with self.assertRaises(TypeError) as e:
                  c = Circle("hello")
              self.assertEqual(str(e.exception), "radius must be a number")
      

      【讨论】:

        【解决方案3】:

        我删除了重复的 self.assertEqual(c.radius,-2.5) 断言,因为它们已经由另一个单元测试 (test_creating_circle_with_numeric_radius(self)) 处理,并导致您的单元测试失败。例如,请参见此处:

            def test_creating_circle_with_negative_radius(self):
                c=Circle(-2.5)
                self.assertEqual(c.radius,-2.5) # Throwing ValueError
                self.assertRaises(ValueError)
        

        在您的代码中,assertEquals(c.radius,-2.5) 尝试确定值 c.radius 是否等于 -2.5。但是没有设置这个值,因为-2.5 超出了可接受的范围,而是抛出了 ValueError 。这可以防止检查self.assertRaises(ValueError)。通过从测试中删除 assertEquals 并将其保留为自己的独立测试,self.assertRaises(ValueError) 将执行并允许您查看代码不符合要求的地方。

        我已更改您的测试以使用上下文管理来捕获引发的异常,从而使测试正常工作

        class TestCircleCreation(unittest.TestCase):
        
            def test_creating_circle_with_numeric_radius(self):
                # Define a circle 'c1' with radius 2.5 and check if 
                # the value of c1.radius equal to 2.5 or not
                c1 = Circle(2.5)        
                self.assertEqual(c1.radius, 2.5)
        
            def test_creating_circle_with_negative_radius(self):
                # Try Defining a circle 'c' with radius -2.5 and see 
                # if it raises a ValueError with the message
                # "radius must be between 0 and 1000 inclusive"
                with self.assertRaises(ValueError) as E:
                    c = Circle(-2.5)     
        
            def test_creating_circle_with_greaterthan_radius(self):
                # Try Defining a circle 'c' with radius 1000.1 and see 
                # if it raises a ValueError with the message
                # "radius must be between 0 and 1000 inclusive"
                with self.assertRaises(ValueError) as E:
                    c = Circle(1000.1)        
        
            def test_creating_circle_with_nonnumeric_radius(self):
                # Try Defining a circle 'c' with radius 'hello' and see 
                # if it raises a TypeError with the message
                # "radius must be a number"
                with self.assertRaises(TypeError) as E:
                    c = Circle('hello')        
        

        虽然这也行得通:

        class TestCircleCreation(unittest.TestCase):
        
            def test_creating_circle_with_numeric_radius(self):
                c1 = Circle(2.5)        
                self.assertEqual(c1.radius, 2.5)
        
            def test_creating_circle_with_negative_radius(self):
                self.assertRaises(ValueError, Circle, -2.5)
        
            def test_creating_circle_with_greaterthan_radius(self):
                self.assertRaises(ValueError, Circle, 1000.1)
        
        
            def test_creating_circle_with_nonnumeric_radius(self):
                self.assertRaises(TypeError, Circle, 'hello')
        

        PS:我建议您在写入文件时使用with open,就像在读取文件时一样。

            with open('output.txt', 'w') as fptr:
                runner = unittest.TextTestRunner(fptr)
                unittest.main(testRunner=runner, exit=False)
        

        您的支票可以从

        简化
                try:
                    if not isinstance(radius, (int, float)):
                        raise TypeError 
                    elif 1000 >=radius>=0:
                            self.radius=radius 
                    else:
                        raise ValueError        
                except ValueError:
                    raise ValueError("radius must be between 0 and 1000 inclusive")
                except TypeError:
                    raise TypeError("radius must be a number")
        

        到:

                    if not isinstance(radius, (int, float)):
                        raise TypeError("radius must be a number")
        
                    if not 1000 >=radius>=0:
                        raise ValueError("radius must be between 0 and 1000 inclusive")
        
                    self.radius=radius 
        

        【讨论】:

        • 我得到的答案是 4 1 0 1 0 1 0 1 但它仍然说你的答案不正确,它对 assertEqual 进行了三项检查,其中出现了异常,看起来这是导致问题的原因请建议
        • 什么说答案不正确?什么是三个 assertEqual 检查?
        • 在上述问题中,doc2、doc3、doc4 的 assert_count1 需要 assertEqual 计数。
        • 第一段声明 assertEquals 检查被删除,只留下独立检查。我现在添加了更多细节来解释这一点。
        【解决方案4】:
        import inspect
        import re
        import unittest
        import math
        
        # Define below the class 'Circle' and it's methods with proper doctests.
        class Circle:
            def __init__(self, radius):
                # Define the initialization method below
                try:
                    if not isinstance(radius, (int, float)):
                        raise TypeError 
                    elif 1000 >=radius>=0:
                            self.radius=radius 
                    else:
                        raise ValueError        
                except ValueError:
                    raise ValueError("radius must be between 0 and 1000 inclusive")
                except TypeError:
                    raise TypeError("radius must be a number")
        
            def area(self):
                # Define the area functionality below
                y=math.pi*(self.radius**2)
                return round(y,2)
        
            def circumference(self):
                # Define the circumference functionality below
                x=math.pi*2*self.radius
                return round(x,2)
        
        class TestCircleCreation(unittest.TestCase):
            def test_creating_circle_with_numeric_radius(self):
                # Define a circle 'c1' with radius 2.5 and check if 
                # the value of c1.radius equal to 2.5 or not
                c1 = Circle(2.5)        
                self.assertEqual(c1.radius, 2.5)
        
            def test_creating_circle_with_negative_radius(self):
                # Try Defining a circle 'c' with radius -2.5 and see 
                # if it raises a ValueError with the message
                # "radius must be between 0 and 1000 inclusive"
                c1 = Circle(2.5)        
                self.assertEqual(c1.radius, 2.5)
        
                with self.assertRaises(ValueError) as E:
                    c = Circle(-2.5)     
        
            def test_creating_circle_with_greaterthan_radius(self):
                # Try Defining a circle 'c' with radius 1000.1 and see 
                # if it raises a ValueError with the message
                # "radius must be between 0 and 1000 inclusive"
        
                c1 = Circle(2.5)        
                self.assertEqual(c1.radius, 2.5)
                with self.assertRaises(ValueError) as E:
                    c = Circle(1000.1)        
        
            def test_creating_circle_with_nonnumeric_radius(self):
                # Try Defining a circle 'c' with radius 'hello' and see 
                # if it raises a TypeError with the message
                # "radius must be a number"
        
                c1 = Circle(2.5)        
                self.assertEqual(c1.radius, 2.5)
        
                with self.assertRaises(TypeError) as E:
                    c = Circle('hello')        
        
        if __name__ == '__main__':
            fptr = open('output.txt', 'w')
        
            runner = unittest.TextTestRunner(fptr)
        
            unittest.main(testRunner=runner, exit=False)
        
            fptr.close()
        
            with open('output.txt') as fp:
                output_lines = fp.readlines()
        
            pass_count = [ len(re.findall(r'\.', line)) for line in output_lines if line.startswith('.')
                             and line.endswith('.\n')]
        
            pass_count = pass_count[0]
            print(str(pass_count))
        
            doc1 = inspect.getsource(TestCircleCreation.test_creating_circle_with_numeric_radius)
            doc2 = inspect.getsource(TestCircleCreation.test_creating_circle_with_negative_radius)
            doc3 = inspect.getsource(TestCircleCreation.test_creating_circle_with_greaterthan_radius)
            doc4 = inspect.getsource(TestCircleCreation.test_creating_circle_with_nonnumeric_radius)
        
            assert1_count = len(re.findall(r'assertEqual', doc1))
        
            print(str(assert1_count))
        
            assert1_count = len(re.findall(r'assertEqual', doc2))
            assert2_count = len(re.findall(r'assertRaises', doc2))
            print(str(assert1_count), str(assert2_count))
        
            assert1_count = len(re.findall(r'assertEqual', doc3))
            assert2_count = len(re.findall(r'assertRaises', doc3))
            print(str(assert1_count), str(assert2_count))
        
            assert1_count = len(re.findall(r'assertEqual', doc4))
            assert2_count = len(re.findall(r'assertRaises', doc4))
            print(str(assert1_count), str(assert2_count))
        

        【讨论】:

        • 试试这个你会得到 4 1 1,1 1,1 1,1
        猜你喜欢
        • 2012-01-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-02
        • 1970-01-01
        • 1970-01-01
        • 2023-03-11
        相关资源
        最近更新 更多