使用 Python Mock 类进行单元测试

发表于:2017-7-21 13:49

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Garfielt    来源:51Testing软件测试网采编

  Listing Twelve演示了assert_has_calls()断言。在18~20行,我调用了三个方法,提供了两个输入。然后,我准备了一个期望调用的列表(fooCalls)并把这个列表传入assert_has_calls()(22~23行)。由于列表匹配了方法的调用,断言通过。
  Listing Twelve
  from mock import Mock, call
   
  # The mock specification
  class Foo(object):
      _fooValue = 123
       
      def callFoo(self):
          pass
       
      def doFoo(self, argValue):
          pass
   
  # create the mock object
  mockFoo = Mock(spec = Foo)
  print mockFoo
  # returns <Mock spec='Foo' id='507120'>
   
  mockFoo.callFoo()
  mockFoo.doFoo("narf")
  mockFoo.doFoo("zort")
   
  fooCalls = [call.callFoo(), call.doFoo("narf"), call.doFoo("zort")]
  mockFoo.assert_has_calls(fooCalls)
  # assert passes
   
  fooCalls = [call.callFoo(), call.doFoo("zort"), call.doFoo("narf")]
  mockFoo.assert_has_calls(fooCalls)
  # AssertionError: Calls not found.
  # Expected: [call.callFoo(), call.doFoo('zort'), call.doFoo('narf')]
  # Actual: [call.callFoo(), call.doFoo('narf'), call.doFoo('zort')]
   
  fooCalls = [call.callFoo(), call.doFoo("zort"), call.doFoo("narf")]
  mockFoo.assert_has_calls(fooCalls, any_order = True)
  # assert passes
  在第26行,我交换了两个doFoo()调用的顺序。第一个doFoo()获得"zort"的输入,第二个获得了"narf"。如果我传入这个fooCalls到assert_has_calls()(第27行)中,断言失败。但是如果我给参数any_order传入参数True,断言通过。这是因为断言将忽略方法调用的顺序。
  Listing Thirteen演示了其他的用法。在fooCalls列表中,我添加了不存在的方法dooFoo()(第22行)。然后我传入fooCalls到assert_has_calls()中(第24行)。断言失败,通知我期望调用的顺序和真实发生的顺序不匹配。如果我给any_order赋值为True(第30行),断言名称dooFoo()作为违规的方法调用。
  Listing Thirteen
  from mock import Mock, call
   
  # The mock specification
  class Foo(object):
      _fooValue = 123
       
      def callFoo(self):
          pass
       
      def doFoo(self, argValue):
          pass
   
  # create the mock object
  mockFoo = Mock(spec = Foo)
  print mockFoo
  # returns <Mock spec='Foo' id='507120'>
   
  mockFoo.callFoo()
  mockFoo.doFoo("narf")
  mockFoo.doFoo("zort")
   
  fooCalls = [call.callFoo(), call.dooFoo("narf"), call.doFoo("zort")]
   
  mockFoo.assert_has_calls(fooCalls)
  # AssertionError: Calls not found.
  # Expected: [call.callFoo(), call.dooFoo('narf'), call.doFoo('zort')]
  # Actual: [call.callFoo(), call.doFoo('narf'), call.doFoo('zort')]
   
  fooCalls = [call.callFoo(), call.dooFoo("narf"), call.doFoo("zort")]
  mockFoo.assert_has_calls(fooCalls, any_order = True)
  # AssertionError: (call.dooFoo('narf'),) not all found in call list
  在assert_has_calls()的两个例子中,注意到关键字call是出现在每个方法的前面。这个关键字是一个helper对象,标记出mock对象的方法属性。为了使用call关键字,请确保使用如下的方法从mocke模块导入helper:
  from mock import Mock, call
  管理Mock
  Mock类的第三套方法允许你控制和管理mock对象。你可以更改mock的行为,改变它的属性或者将mock恢复到测试前的状态。你甚至可以更改每个mock方法或者mock本身的响应值。attach_mock()方法让你在mock中添加第二个mock对象。这个方法带有两个参数:第二个mock对象(aMock)和一个属性名称(aName)。
  Listing Fourteen 样式了attach_mock()方法的使用。那儿,我创建了两个mock对象mockFoo和mockBar,他们有不同spec参数(第25行和第30行)。我用attach_mock()方法将mockBar添加到mockFoo中,命名为fooBar(第35行)。一旦添加成功,我就能通过property fooBar访问第二mock对象和它的属性(46~53行)。并且我仍然可以访问第一个mock对象mockFoo的属性。
  Listing Fourteen
  from mock import Mock
  # The mock object
  class Foo(object):
  # instance properties
  _fooValue = 123
  def callFoo(self):
  print "Foo:callFoo_"
  def doFoo(self, argValue):
  print "Foo:doFoo:input = ", argValue
  class Bar(object):
  # instance properties
  _barValue = 456
  def callBar(self):
  pass
  def doBar(self, argValue):
  pass
  # create the first mock object
  mockFoo = Mock(spec = Foo)
  print mockFoo
  # returns <Mock spec='Foo' id='507120'>
  # create the second mock object
  mockBar = Mock(spec = Bar)
  print mockBar
  # returns: <Mock spec='Bar' id='2784400'>
  # attach the second mock to the first
  mockFoo.attach_mock(mockBar, 'fooBar')
  # access the first mock's attributes
  print mockFoo
  # returns: <Mock spec='Foo' id='495312'>
  print mockFoo._fooValue
  # returns: <Mock name='mock._fooValue' id='428976'>
  print mockFoo.callFoo()
  # returns: <Mock name='mock.callFoo()' id='448144'>
  # access the second mock and its attributes
  print mockFoo.fooBar
  # returns: <Mock name='mock.fooBar' spec='Bar' id='2788592'>
  print mockFoo.fooBar._barValue
  # returns: <Mock name='mock.fooBar._barValue' id='2788016'>
  print mockFoo.fooBar.callBar()
  # returns: <Mock name='mock.fooBar.callBar()' id='2819344'>
  print mockFoo.fooBar.doBar("narf")
  # returns: <Mock name='mock.fooBar.doBar()' id='4544528'>
  configure_mock()方法让你批量的更改mock对象。它唯一的参数是一个键值对序列,每个键就是你想要修改的属性。如果你的对象没有指定的属性,configure_mock()将在mock中添加属性。
  Listing fifteen显示了configure_mock()方法的运用。再次,我定义了一个spec为类Foo和return_value为555的mock对象mockFoo(第13行)。然后使用configure_mock()方法更改return_value为999(第17行)。当我直接调用mockFoo时,获得的结果为999,替换了原来的555。
  Listing Fifteen
  from mock import Mock
   
  class Foo(object):
      # instance properties
      _fooValue = 123
       
      def callFoo(self):
          print "Foo:callFoo_"
       
      def doFoo(self, argValue):
          print "Foo:doFoo:input = ", argValue
   
  mockFoo = Mock(spec = Foo, return_value = 555)
  print mockFoo()
  # returns: 555
   
  mockFoo.configure_mock(return_value = 999)
  print mockFoo()
  # returns: 999
   
  fooSpec = {'callFoo.return_value':"narf", 'doFoo.return_value':"zort", 'doFoo.side_effect':StandardError}
  mockFoo.configure_mock(**fooSpec)
   
  print mockFoo.callFoo()
  # returns: narf
  print mockFoo.doFoo("narf")
  # raises: StandardError
   
  fooSpec = {'doFoo.side_effect':None}
  mockFoo.configure_mock(**fooSpec)
  print mockFoo.doFoo("narf")
  # returns: zort
  接着,我准备了一个字段对象(fooSpec),对两个mock方法设置了返回值,为doFoo()设置了side_effect(第21行)。我将fooSpec传入configure_mock(),注意fooSpec带有前缀'**'(第22行)。现在调用callFoo()结果返回“narf”。调用doFoo(),无论输入什么,引发StandardError 信号(行24~27)。如果我修改了fooSpec,设置doFoo()的side_effect的值为None,当我调用doFoo()时,将得到结果“zort”(29~32行)。
  下一个方法mock_add_spec()让你向mock对象添加新的属性。除了mock_add_spec()工作在一个已存在的对象上之外,它的功能类似于构造器的spec参数。它擦除了一些构造器设置的属性。这个方法带有两个参数:spec属性(aSpec)和spc_set标志(aFlag)。再次,spce可以是字符串列表或者是类。已添加的属性缺省状态是只读的,但是通过设置spec_set标志为True,可以让属性可写。
  Listing Sixteen演示了mock_add_spec()的运用。mock对象mockFoo开始的属性来自于类Foo(第25行)。当我访问两个属性(_fooValue和callFoo())时,我得到结果确认他们是存在的(29~32行)。
  Listing Sixteen
  from mock import Mock
   
  # The class interfaces
  class Foo(object):
      # instance properties
      _fooValue = 123
       
      def callFoo(self):
          print "Foo:callFoo_"
       
      def doFoo(self, argValue):
          print "Foo:doFoo:input = ", argValue
   
  class Bar(object):
      # instance properties
      _barValue = 456
       
      def callBar(self):
          pass
       
      def doBar(self, argValue):
          pass
       
  # create the mock object
  mockFoo = Mock(spec = Foo)
   
  print mockFoo
  # returns <Mock spec='Foo' id='507120'>
  print mockFoo._fooValue
  # returns <Mock name='mock._fooValue' id='2788112'>
  print mockFoo.callFoo()
  # returns: <Mock name='mock.callFoo()' id='2815376'>
   
  # add a new spec attributes
  mockFoo.mock_add_spec(Bar)
   
  print mockFoo
  # returns: <Mock spec='Bar' id='491088'>
  print mockFoo._barValue
  # returns: <Mock name='mock._barValue' id='2815120'>
  print mockFoo.callBar()
  # returns: <Mock name='mock.callBar()' id='4544368'>
   
  print mockFoo._fooValue
  # raises: AttributeError: Mock object has no attribute '_fooValue'
  print mockFoo.callFoo()
  # raises: AttributeError: Mock object has no attribute 'callFoo'
  然后,我使用mock_add_spec()方法添加类Bar到mockFoo(第35行)。mock对象现在的属性已声明在类Bar中(39~42行)。如果我访问任何Foo属性,mock对象将引发AttributeError 信号,表示他们不存在(44~47行)。
  最后一个方法resetMock(),恢复mock对象到测试前的状态。它清除了mock对象的调用统计和断言。它不会清除mock对象的return_value和side_effect属性和它的方法属性。这样做是为了重新使用mock对象避免重新创建mock的开销。
  最后,你能给每个方法属性分配返回值或者side-effect。你能通过return_value和side_effect访问器做到这些。例如,按如下的语句通过return_value访问器设置方法callFoo()的返回值为"narf":
  mockFoo.callFoo.return_value = "narf"
  按如下的语句通过side_effect访问器 设置方法callFoo()的side-ffect为TypeError
  mockFoo.callFoo.side_effect = TypeError
  传入None清除side-effect
  mockFoo.callFoo.side_effect = None
  你也可以用这个两个相同的访问器改变mock对象对工厂调用的响应值
  Mock统计
  最后一套方法包含跟踪mock对象所做的任意调用的访问器。当mock对象获得工厂调用时,访问器called返回True,否则返回False。查看Listing Seventeen中的代码,我创建了mockFoo之后,called访问器返回了结果False(19~20行)。如果我做了一个工厂调用,它将返回结果True(22~23行)。但是如果我创建了第二个mock对象,然后调用了mock方法callFoo()(第30行)?在这个例子中,called访问器仅仅放回了False结果(31~32行)。
  Listing Seventeen
  from mock import Mock
   
  # The mock object
  class Foo(object):
      # instance properties
      _fooValue = 123
       
      def callFoo(self):
          print "Foo:callFoo_"
       
      def doFoo(self, argValue):
          print "Foo:doFoo:input = ", argValue
   
  # create the first mock object
  mockFoo = Mock(spec = Foo)
  print mockFoo
  # returns <Mock spec='Foo' id='507120'>
   
  print mockFoo.called
  # returns: False
   
  mockFoo()
  print mockFoo.called
  # returns: True
   
  mockFoo = Mock(spec = Foo)
  print mockFoo.called
  # returns: False
   
  mockFoo.callFoo()
  print mockFoo.called
  # returns: False
  访问器call_count给出了mock对象被工厂调用的次数。查看Listing Eighteen中的代码。我创建mockFoo之后,call_count给出的期望结果为0(19~20行)。当我对mockFoo做了一个工厂调用时,call_count增加1(22~24行)。当我调用mock方法callFoo()时,call_count没有改变(26~28行)。如果我做了第二次工厂调用call_count将再增加1。
  Listing Eighteen
  from mock import Mock
   
  # The mock object
  class Foo(object):
      # instance properties
      _fooValue = 123
       
      def callFoo(self):
          print "Foo:callFoo_"
       
      def doFoo(self, argValue):
          print "Foo:doFoo:input = ", argValue
   
  # create the first mock object
  mockFoo = Mock(spec = Foo)
  print mockFoo
  # returns <Mock spec='Foo' id='507120'>
   
  print mockFoo.call_count
  # returns: 0
   
  mockFoo()
  print mockFoo.call_count
  # returns: 1
   
  mockFoo.callFoo()
  print mockFoo.call_count
  # returns: 1
  访问器call_args返回工厂调用已用的参数。Listing Nineteen演示了它的运用。对于新创建的mock对象(mockFoo),call_args访问器返回结果为None(17~21行)。如果我做了一个工厂调用,在输入中传入"zort",call_args报告的结果为call('zort')(23~25行)。注意结果中的call关键字。对于第二个没有输入的工厂调用,call_args返回call()(27~29行)。第三个工厂调用,输入“troz”,call_args给出结果为call('troz')(31~33行)。但是当我调用mock方法callFoo()时,call_args访问器仍然返回call('troz')(35~37行)。
  Listing Nineteen
  #!/usr/bin/python
   
  from mock import Mock
   
  # The mock object
  class Foo(object):
      # instance properties
      _fooValue = 123
       
      def callFoo(self):
          print "Foo:callFoo_"
       
      def doFoo(self, argValue):
          print "Foo:doFoo:input = ", argValue
   
  # create the first mock object
  mockFoo = Mock(spec = Foo, return_value = "narf")
  print mockFoo
  # returns <Mock spec='Foo' id='507120'>
  print mockFoo.call_args
  # returns: None
   
  mockFoo("zort")
  print mockFoo.call_args
  # returns: call('zort')
   
  mockFoo()
  print mockFoo.call_args
  # returns: call()
   
  mockFoo("troz")
  print mockFoo.call_args
  # returns: call('troz')
   
  mockFoo.callFoo()
  print mockFoo.call_args
  # returns: call('troz')
  访问器call_args_list 也报告了工厂调用中已使用的参数。但是call_args返回最近使用的参数,而call_args_list返回一个列表,第一项为最早的参数。Listing Twenty显示了这个访问的的运用,使用了和Listing Nineteen相同的代码。
  Listing Twenty
  from mock import Mock
   
  # The mock object
  class Foo(object):
      # instance properties
      _fooValue = 123
       
      def callFoo(self):
          print "Foo:callFoo_"
       
      def doFoo(self, argValue):
          print "Foo:doFoo:input = ", argValue
   
  # create the first mock object
  mockFoo = Mock(spec = Foo, return_value = "narf")
  print mockFoo
  # returns <Mock spec='Foo' id='507120'>
   
  mockFoo("zort")
  print mockFoo.call_args_list
  # returns: [call('zort')]
   
  mockFoo()
  print mockFoo.call_args_list
  # returns: [call('zort'), call()]
   
  mockFoo("troz")
  print mockFoo.call_args_list
  # returns: [call('zort'), call(), call('troz')]
   
  mockFoo.callFoo()
  print mockFoo.call_args_list
  # returns: [call('zort'), call(), call('troz')]
  访问器mothod_calls报告了测试对象所做的mock方法的调用。它的结果是一个列表对象,每一项显示了方法的名称和它的参数。
  Listing Twenty-one演示了method_calls的运用。对新创建的mockFoo,method_calls返回了空列表(15~19行)。当做了工厂调用时,同样返回空列表(21~23行)。当我调用了mock方法callFoo()时,method_calls返回一个带一项数据的列表对象(25~27行)。当我调用doFoo(),并传入"narf"参数时,method_calls返回带有两项数据的列表(29~31行)。注意每个方法名称是按照它调用的顺序显示的。
  Listing Twenty-one
  from mock import Mock
   
  # The mock object
  class Foo(object):
      # instance properties
      _fooValue = 123
       
      def callFoo(self):
          print "Foo:callFoo_"
       
      def doFoo(self, argValue):
          print "Foo:doFoo:input = ", argValue
   
  # create the first mock object
  mockFoo = Mock(spec = Foo, return_value = "poink")
  print mockFoo
  # returns <Mock spec='Foo' id='507120'>
  print mockFoo.method_calls
  # returns []
   
  mockFoo()
  print mockFoo.method_calls
  # returns []
   
  mockFoo.callFoo()
  print mockFoo.method_calls
  # returns: [call.callFoo()]
   
  mockFoo.doFoo("narf")
  print mockFoo.method_calls
  # returns: [call.callFoo(), call.doFoo('narf')]
   
  mockFoo()
  print mockFoo.method_calls
  # returns: [call.callFoo(), call.doFoo('narf')]
  最后一个访问器mock_calls报告了测试对象对mock对象所有的调用。结果是一个列表,但是工厂调用和方法调用都显示了。Listing Twenty-two演示这个访问器的运用,使用了和Listing Twenty-one相同的代码
  Listing Twenty-two
  from mock import Mock
   
  # The mock object
  class Foo(object):
      # instance properties
      _fooValue = 123
       
      def callFoo(self):
          print "Foo:callFoo_"
       
      def doFoo(self, argValue):
          print "Foo:doFoo:input = ", argValue
   
  # create the first mock object
  mockFoo = Mock(spec = Foo, return_value = "poink")
  print mockFoo
  # returns <Mock spec='Foo' id='507120'>
   
  print mockFoo.mock_calls
  # returns []
   
  mockFoo()
  print mockFoo.mock_calls
  # returns [call()]
   
  mockFoo.callFoo()>
  print mockFoo.mock_calls
  # returns: [call(), call.callFoo()]
   
  mockFoo.doFoo("narf")
  print mockFoo.mock_calls
  # returns: [call(), call.callFoo(), call.doFoo('narf')]
   
  mockFoo()
  print mockFoo.mock_calls
  # returns: [call(), call.callFoo(), call.doFoo('narf'), call()]
  在测试中使用MOCK
  数据类型,模型或者节点,这些是mock对象可能被假定的一些角色。但是mock对象怎样适合单元测试呢?让我们一起来看看,来自Martin Fowler的文章Mocks Aren't Stubs采取了简化的设置。
  在这个测试中,设置了三个类(图4)。Order类是测试对象。它模拟了单一项目的采购订单,订单来源于一个数据源。Warehouse类是测试资源。它包含了键值对的序列,键是项目的名称,值是可用的数量。OrderTest类是测试用例本身。
  图4
  Listing Twenty-three描述了Order。Order类声明了三个属性:项目名称(_orderItem),要求的数量(_orderAmount)和已填写的数量(_orderFilled)。它的构造器带有两个参数(8~18行),填入的属性是_orderItem和_orderAmount。它的__repr__()方法返回了购买清单的摘要(21~24行)。
  Listing Twenty-three
  class Order(object):
      # instance properties
      _orderItem = "None"
      _orderAmount = 0
      _orderFilled = -1
       
      # Constructor
      def __init__(self, argItem, argAmount):
          print "Order:__init__"
           
          # set the order item
          if (isinstance(argItem, str)):
              if (len(argItem) > 0):
                  self._orderItem = argItem
           
          # set the order amount
          if (argAmount > 0):
              self._orderAmount = argAmount
           
      # Magic methods
      def __repr__(self):
         # assemble the dictionary
          locOrder = {'item':self._orderItem, 'amount':self._orderAmount}
          return repr(locOrder)
       
      # Instance methods
      # attempt to fill the order
      def fill(self, argSrc):
          print "Order:fill_"
           
          try:
              # does the warehouse has the item in stock?
              if (argSrc is not None):
                  if (argSrc.hasInventory(self._orderItem)):
                      # get the item
                      locCount =    argSrc.getInventory(self._orderItem, self._orderAmount)
                   
                      # update the following property
                      self._orderFilled = locCount
                  else:
                      print "Inventory item not available"
              else:
                  print "Warehouse not available"
          except TypeError:
              print "Invalid warehouse"
       
      # check if the order has been filled
      def isFilled(self):
          print "Order:isFilled_"
          return (self._orderAmount == self._orderFilled)
  Order类定义了两个实例方法。fill()方法从参数(argSrc)中获取数据源。它检查数据源是否可用,数据源的项目是否存在问题(33~34行)。它提交了一个申请并用实际返回的数量更新_orderFilled(36~39行)。当_orderAmount和_orderFilled有相同的值时,isFilled()方法返回True(48~50行)。
  Listing Twenty-four描述了Warehouse类。它是一个抽象类,声明了属性和方法接口,但是没有定义方法本身。属性_houseName是仓库的名字,而_houseList是它持有的库存。还有这两个属性的访问器。
  Listing Twenty-four
  class Warehouse(object):    
      # private properties
      _houseName = None
      _houseList = None
           
      # accessors
      def warehouseName(self):
          return (self._houseName)
       
      def inventory(self):
          return (self._houseList)
       
       
      # -- INVENTORY ACTIONS
      # set up the warehouse
      def setup(self, argName, argList):
      &#9;pass
       
      # check for an inventory item
      def hasInventory(self, argItem):
          pass
       
      # retrieve an inventory item
      def getInventory(self, argItem, argCount):
          pass
           
      # add an inventory item
      def addInventory(self, argItem, argCount):
          pass
  Warehouse类声明了四个方法接口。方法setup()带有两个参数,是为了更新这两个属性。方法hasInventory()参数是项目的名称,如果项目在库存中则返回True。方法getInventory()的参数是项目的名称和数量。它尝试着从库存中扣除数量,返回哪些是成功的扣除。方法addInventory()的参数也是项目名称和数量。它将用这两个参数更新_houseList。
  Listing Twenty-five是测试用例本身,orderTest类。他有一个属性fooSource是Order类所需的mock对象。setUp()方法识别执行的测试例程(14~16行),然后创建和配置mock对象(21~34行)。tearDown()方法向stdout打印一个空行。
  Listing Twenty-five
  import unittest
  from mock import Mock, call
   
  class OrderTest(unittest.TestCase):
      # declare the test resource
      fooSource = None
       
      # preparing to test
      def setUp(self):
          """ Setting up for the test """
          print "OrderTest:setUp_:begin"
           
          # identify the test routine
          testName = self.id().split(".")
          testName = testName[2]
          print testName
           
          # prepare and configure the test resource
          if (testName == "testA_newOrder"):
              print "OrderTest:setup_:testA_newOrder:RESERVED"
          elif (testName == "testB_nilInventory"):
              self.fooSource = Mock(spec = Warehouse, return_value = None)
          elif (testName == "testC_orderCheck"):
              self.fooSource = Mock(spec = Warehouse)
              self.fooSource.hasInventory.return_value = True
              self.fooSource.getInventory.return_value = 0
          elif (testName == "testD_orderFilled"):
              self.fooSource = Mock(spec = Warehouse)
              self.fooSource.hasInventory.return_value = True
              self.fooSource.getInventory.return_value = 10
          elif (testName == "testE_orderIncomplete"):
              self.fooSource = Mock(spec = Warehouse)
              self.fooSource.hasInventory.return_value = True
              self.fooSource.getInventory.return_value = 5
          else:
              print "UNSUPPORTED TEST ROUTINE"
       
      # ending the test
      def tearDown(self):
          """Cleaning up after the test"""
          print "OrderTest:tearDown_:begin"
          print ""
       
      # test: new order
      # objective: creating an order
      def testA_newOrder(self):
          # creating a new order
          testOrder = Order("mushrooms", 10)
          print repr(testOrder)
           
          # test for a nil object
          self.assertIsNotNone(testOrder, "Order object is a nil.")
           
          # test for a valid item name
          testName = testOrder._orderItem
          self.assertEqual(testName, "mushrooms", "Invalid item name")
           
          # test for a valid item amount
          testAmount = testOrder._orderAmount
          self.assertGreater(testAmount, 0, "Invalid item amount")
       
      # test: nil inventory
      # objective: how the order object handles a nil inventory
      def testB_nilInventory(self):
          """Test routine B"""
          # creating a new order
          testOrder = Order("mushrooms", 10)
          print repr(testOrder)
           
          # fill the order
          testSource = self.fooSource()
          testOrder.fill(testSource)
           
          # print the mocked calls
          print self.fooSource.mock_calls
           
          # check the call history
          testCalls = [call()]
          self.fooSource.assert_has_calls(testCalls)
       
      # ... continued in the next listing
  OrderTest类有五个测试例程。所有五个测试例程在开始的时候都创建了一个Order类的实例。例程testA_newOrder()测试Order对象是否可用是否有正确的数据(46~60行)。例程testB_nilWarehouse()创建了一个空的mock并传入Order对象的fill()方法(64~79行)。它检查了mock的调用历史,确保仅仅发生了工厂调用。
  例程testC_orderCheck()(Listing Twenty-six)测试了Order对象在库存不足时的反应。最初,fooSource的hasInventory()方法响应True,getinventory()方法返回0。测试例程检查是否订单未达成,是否正确的mock方法被带调用(16~19行)。然后测试例程创建了一个新的Order对象,这次是一个不同的项目。mock(fooSource)的方法hasInventory()的响应设置为False(第27行)。再次,例程检查是否订单未达成,是否调用了正确的mock方法(34~37行)。注意使用reset_mock()方法将fooSource恢复到测试前的状态(第28行)
  Listing Twenty-six
  class OrderTest(unittest.TestCase):
      # ... see previous listing
       
      # test: checking the inventory
      # objective: does the order object check for inventory?
      def testC_orderCheck(self):
          """Test routine C"""
          # creating a test order
          testOrder = Order("mushrooms", 10)
          print repr(testOrder)
           
          # perform the test
          testOrder.fill(self.fooSource)
           
          # perform the checks
          self.assertFalse(testOrder.isFilled())
          self.assertEqual(testOrder._orderFilled, 0)
           
          self.fooSource.hasInventory.assert_called_once_with("mushrooms")
          print self.fooSource.mock_calls
           
          # creating another order
          testOrder = Order("cabbage", 10)
          print repr(testOrder)
           
          # reconfigure the test resource
          self.fooSource.hasInventory.return_value = False
          self.fooSource.reset_mock()
           
          # perform the test
          testOrder.fill(self.fooSource)
           
          # perform the checks
          self.assertFalse(testOrder.isFilled())
          self.assertEqual(testOrder._orderFilled, -1)
           
          self.fooSource.hasInventory.assert_called_once_with("cabbage")
          print self.fooSource.mock_calls
       
      # ... continued in the next listing
  测试例程testD_orderFilled()(Listing Twenty-seven)模拟了一个成功的订单事务。fooSource的hasInventory()方法响应True,getinventory()方法返回10。例程调用fill()方法传入mock对象,然后检查订单是否已完成(17~18行)。它也检查了是否采用正确的顺序和正确的参数调用了 正确的mock方法(20~24行)。
  Listing Twenty-seven
  # ... see previous listing
       
      # test: fulfilling an order
      # objective: how does the order object behave with a successful transaction
      def testD_orderFilled(self):
          """Test routine D"""
          # creating a test order
          testOrder = Order("mushrooms", 10)
          print repr(testOrder)
           
          # perform the test
          testOrder.fill(self.fooSource)
          print testOrder.isFilled()
           
      
22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号