Mock數據


楔子

現在, 要測試這些接口:

"""
用例集
case_set.py
pip install requests
"""
import requests

def v2ex_info():
    """
    獲取v2ex的網站信息
    https://www.v2ex.com/api/site/info.json

    """
    response = requests.get(url='https://www.v2ex.com/api/site/info.json')
    return response.json().get('title')  # V2EX

def v2ex_stats():
    """
    獲取v2ex的網站信息
    https://www.v2ex.com/api/site/stats.json
    """
    response = requests.get(url='https://www.v2ex.com/api/site/stats.json')
    return response.json().get('member_max')  # int類型

def cnodejs():
    """ 獲取 cnodejs,推薦博客總數 """
    response = requests.get('https://cnodejs.org/api/v1/topics')
    return response.json().get('success')  # True

if __name__ == '__main__':
    print(v2ex_info() == 'V2EX')
    print(type(v2ex_stats()) is int)
    print(cnodejs() is True)
View Code

關於requests模塊, see also:https://www.cnblogs.com/sundawei7/p/11949153.html

規則是:

  • v2ex_info接口返回值中的title是V2EX才算通過。
  • v2ex_stats接口返回值中的member_max是int類型才算通過。
  • cnodejs接口只值中的success是True算通過。

根據規則很快的寫出了測試用例:

"""
用例類
myMain.py
"""

import unittest
from case_set import v2ex_stats, v2ex_info, cnodejs

class InterfaceCase(unittest.TestCase):

    def test_v2ex_stats(self):
        """ 測試 v2ex_stats 接口,返回: int類型"""
        self.assertIs(type(v2ex_stats()), int)

    def test_v2ex_info(self):
        """ 測試 v2ex_info 接口, 返回: V2EX """
        self.assertEqual(v2ex_info(), 'V2EX')

    def test_cnodejs(self):
        """ 測試 cnodejs 接口,返回: True """
        self.assertIs(cnodejs(), True)

if __name__ == '__main__':
    unittest.main()
View Code

結果也OK:

M:\tests>python36 myMain.py -v
test_cnblogs_info (__main__.InterfaceCase)
測試博客園接口,返回: 200 ... ok
test_v2ex_info (__main__.InterfaceCase)
測試 v2ex_info 接口, 返回: V2EX ... ok
test_v2ex_stats (__main__.InterfaceCase)
測試 v2ex_stats 接口,返回: int類型 ... ok

----------------------------------------------------------------------
Ran 3 tests in 3.075s

OK
View Code

為什么需要mock

在反復的執行測試用例時,發現test_cnodejs用例執行有些問題。有時候執行失敗有時候成功,並且就算成功也響應時間較長,一番分析后,發現不是自己的問題,是接口暫時開發的不是很完善,導致現在測試不穩定。 但是你根據接口文檔知道,這個接口這么測試,只要返回True就算通過。 那么能不能我們自己模擬出來這么一個接口,然后模擬一些方法和數據,在測試環境下使用。

什么是mock 在協同開發、測試中,總會出現各種問題,比如:

  • 開發人員某些接口還沒有開發完畢。
  • 與第三方聯調時,第三方拖了后腿,沒准備好環境、數據都有可能。比如說我們測試的某個接口本身沒有問題,但它依賴的某個接口有些問題,這就影響我們的正常測試任務進度。
  • 測試環境惡劣。
  • 開發只提供接口,數據自己搞!

這些問題總能影響我們的測試進度,那么我們怎么正常的展開呢? 這就需要mock來解決了。

什么是mock mock是在測試過程中,對於一些不容易構造/獲取的對象,創建一個mock對象來模擬對象的行為。 mock測試一般也稱為mock數據。 簡單來說,mock就是向測試對象提供一套和測試資源完全相同的接口和方法,不關系具體的實現過程,只關心具體結果。 mock測試的優點

  • 團隊並行工作:有了mock,前后端人員只需要定義好接口文檔就可以開始並行的工作,互不影響,只需要在最后聯調的時候多多交流即可。后端與后端之間如果有接口耦合,也同樣能被Mock解決;測試過程中如果遇到依賴接口沒有准備好,同樣可以借助Mock;不會出現一個團隊等待另一個團隊的情況。這樣的話,開發自測階段就可以及早開展,從而發現缺陷的時機也提前了,有利於整個產品質量以及進度的保證。
  • 開啟TDD模式,即測試驅動開發:單元測試是TDD實現的基石,而TDD經常會碰到協同模塊尚未開發完成的情況,但是有了mock,這些一切都不是問題。當接口定義好后,測試人員就可以創建一個Mock,把接口添加到自動化測試環境,提前創建測試。
  • 模擬出無法訪問的資源:比如說,你需要調用一個“牆”外的資源來方便自己調試,就可以自己Mock一個。
  • 系統隔離:假如我們需要調用一個post請求,為了獲得某個響應,來看當前系統是否能正確處理返回的“響應”,但是這個post請求會造成數據庫中數據的污染,那么就可以充分利用Mock,構造一個虛擬的post請求,我們給他指定返回就好了。
  • 產品展示:假如我們需要創建一個演示程序,並且做了簡單的UI,那么在完全沒有開發后端服務的情況下,也可以進行演示。說到演示了,假如你已經做好了一個系統,並且需要給客戶進行演示,但是里面有些真實數據並不想讓用戶看到,那么同樣,你可以用Mock接口把這些敏感信息接口全部替換。
  • 測試覆蓋:假如有一個接口,有100個不同類型的返回,我們需要測試它在不同返回下,系統是否能夠正常響應,但是有些返回在正常情況下基本不會發生,難道你要千方百計地給系統做各種手腳讓他返回以便測試嗎?比如,我們需要測試在當接口發生500錯誤的時候,app是否崩潰,別告訴我你一定要給服務端代碼做些手腳讓他返回500 。。。而使用mock,這一切就都好辦了,想要什么返回就模擬什么返回,媽媽再也不用擔心你的測試覆蓋度了。

關於TDD,see also:https://baike.baidu.com/item/TDD/9064369?fr=aladdin關於測試覆蓋,see also:https://www.cnblogs.com/sundawei7/p/11944489.html

如何mock數據

下載安裝

這里需要用到mock模塊了,在Python3.x中,mock被集成到了unittest中,無需下載,直接導入即可,但在Python2.x中,就需要:

pip install mock 

mock類的構成

這里以Python3.x為例。

快速上手

構造器:_init_

from unittest import mock

mock_obj = mock.Mock()
print(mock_obj)  # <Mock id='10069264'>
print(dir(mock_obj))
'''
[
    'assert_any_call', 'assert_called', 'assert_called_once', 
    'assert_called_once_with', 'assert_called_with', 'assert_has_calls', 
    'assert_not_called', 'attach_mock', 'call_args', 
    'call_args_list', 'call_count', 'called', 
    'configure_mock', 'method_calls', 'mock_add_spec', 
    'mock_calls', 'reset_mock', 'return_value', 
    'side_effect'
]
'''
View Code

雖然__init__是實例化方法,但在這里通常被稱為構造器。 由打印結果可以看到,通過mock.Mock()實例化出一個mock對象mock_obj。這個對象是繼承了Mock類的屬性和方法。這樣的一個mock對象對我們來說用處不大。 我們來試着添加一些自定義屬性和方法,使之更靈活。 在Mock實例化時,我們可以傳入這些參數:

  • name:mock對象的名字。它只是起到標識作用,當你print一個有name的mock對象時,可以看到它的name。
  • spec:mock對象的屬性值。
  • side_effect:該參數指向一個可調用對象(一般是函數),當mock對象被調用時,如果該參數的返回值是默認的DEFAULT,則mock對象返回return_value指定的值,否則返回side_effect指定的對象的返回值。
  • return_value:該參數指定一個值或者對象,當mock對象被調用時,如果side_effect函數的返回值是DEFAULT,那么mock對象返回return_value指定的值或者對象。

注意,如果side_effect和return_value同時存在的時候,side_effect將會覆蓋return_value。

name

from unittest import mock

mock_obj1 = mock.Mock()
mock_obj2 = mock.Mock(name='mock_obj2')
print(mock_obj1)  # <Mock id='50111760'>
print(mock_obj2)  # <Mock name='mock_obj' id='53781776'>
View Code

name參數沒啥好說的,就是跟mock對象起了個名字。

為return_value指定某個值 現在讓我們使用mock來模擬出文章開頭的那幾個接口測試中的cnodejs接口。

import unittest
from unittest import mock
from case_set import cnodejs  # 導入真實的cnodejs接口函數

class CnodejsTestCase(unittest.TestCase):

    def test_mock_cnodejs(self):
        """ 使用 mock 模擬的 cnodejs 接口 返回: True"""
        # 構造mock對象
        cnodejs = mock.Mock(return_value=True)
        # 使用mock對象進行斷言
        self.assertIs(cnodejs(), True)

    def test_cnodejs(self):
        """ 測試 cnodejs 接口,返回: True """
        self.assertIs(cnodejs(), True)


if __name__ == '__main__':
    unittest.main()
View Code

用例test_mock_cnodejs方法中: 在Mock類實例化時傳入return_value參數,然后構造出的mock對象賦值給cnodejs變量。然后cnodejs()相當於調用mock對象,得到返回值True,完事拿着這個返回值使用unittest進行斷言。 用例test_cnodejs方法中,正常寫測試用例斷言,以判斷兩個用例方法有什么不同之處:

test_cnodejs (__main__.CnodejsTestCase)
測試 cnodejs 接口,返回: True ... ok
test_mock_cnodejs (__main__.CnodejsTestCase)
使用 mock 模擬的 cnodejs 接口 返回: True ... ok

----------------------------------------------------------------------
Ran 2 tests in 1.097s

OK
View Code

可以看到,兩個用例方法都通過了,並沒有什么區別。 在測試環境下,使用mock模擬的方法進行測試,這樣能盡早的介入測試,帶來的優勢不一而足。 為return_value指定類的對象 return_value除了上述用法,還可以指定類的對象:

from unittest import mock


class Foo(object):
    """ 自定義類 """

    def f1(self):
        return 'this is Foo.f1'

    def f2(self, name):
        return name
# 正常的類的實例化與調用
foo_obj = Foo()
print(foo_obj.f1())  # this is Foo.f1
print(foo_obj.f2('this is Foo.f2'))  # this is Foo.f2

# 構造mock對象並傳入 Foo實例化對象
foo_Class = mock.Mock(return_value=Foo())
# 想要得到mock對象的返回值,必須調用,也就是加括號
foo_obj = foo_Class()  # mock對象調用得到return_value值也就是Foo的實例化對象
# 接下里就是正常的調用了
print(foo_obj.f1())  # this is Foo.f1
# 同樣可以正常傳參
print(foo_obj.f2('this is Foo.f2'))  # this is Foo.f2
View Code

使用mock對象模擬類的實例化對象同樣方便。

side_effect 先來看第一個示例,可以為mock對象的side_effect參數指定可迭代對象。

from unittest import mock

mock_obj1 = mock.Mock(return_value=100)
print(mock_obj1())  # 100
mock_obj2 = mock.Mock(return_value=100, side_effect=[200, 300])
print(mock_obj2())  # 200
View Code

由上例可以看到,如果在構造mock對象的時候,只有return_value被指定,調用mock對象返回return_value指定的值。 當side_effectreturn_value同時被指定時,side_effect就覆蓋了return_value。 那么既然side_effect接受的是一個可迭代對象,就可以多次調用它:

from unittest import mock

mock_obj1 = mock.Mock(return_value=100)
print(mock_obj1())  # 100
mock_obj2 = mock.Mock(return_value=100, side_effect=[200, 300])
print(mock_obj2())  # 200
print(mock_obj2())  # 300
print(mock_obj2())  # StopIteration
View Code

可以看到side_effect對象本質上是一個生成器。

為spec指定屬性組成的列表 現在使用mock來模擬出來兩V2EX的兩個接口方法。

import unittest
from unittest import mock
from case_set import v2ex_info, v2ex_stats

# 為mock對象的spec參數傳入屬性(方法)組成的列表
spec_list = ['v2ex_info', 'v2ex_stats']
mock_obj = mock.Mock(spec=spec_list)
print(spec_list)  # ['v2ex_info', 'v2ex_stats']
# 根據真實的接口規則設置兩個方法的返回值
mock_obj.v2ex_info.return_value = 'V2EX'
mock_obj.v2ex_stats.return_value = 466668  # 該接口只需要返回值是int即可

class TestCaseDemo(unittest.TestCase):

    def test_v2ex_stats(self):
        """ 測試 v2ex_stats 接口,返回: int類型"""
        self.assertIs(type(v2ex_stats()), int)

    def test_mock_v2ex_stats(self):
        """ mock v2ex_stats 接口,返回: int類型 """
        v2ex_stats = mock_obj.v2ex_stats
        self.assertIs(type(v2ex_stats()), int)

    def test_mock_v2ex_info(self):
        """ mock v2ex_info 接口, 返回: V2EX """
        v2ex_info = mock_obj.v2ex_info
        self.assertEqual(v2ex_info(), 'V2EX')

    def test_v2ex_info(self):
        """ 測試 v2ex_info 接口, 返回: V2EX """
        self.assertEqual(v2ex_info(), 'V2EX')

if __name__ == '__main__':
    unittest.main()
View Code

結果:

M:\tests>python36 myMain.py -v
test_mock_v2ex_info (__main__.TestCaseDemo)
mock v2ex_info 接口, 返回: V2EX ... ok
test_mock_v2ex_stats (__main__.TestCaseDemo)
mock v2ex_stats 接口,返回: int類型 ... ok
test_v2ex_info (__main__.TestCaseDemo)
測試 v2ex_info 接口, 返回: V2EX ... ok
test_v2ex_stats (__main__.TestCaseDemo)
測試 v2ex_stats 接口,返回: int類型 ... ok

----------------------------------------------------------------------
Ran 4 tests in 3.154s

OK
View Code

由結果發現,用mock模擬的兩個接口都通過了。 為spec指定類屬性

from unittest.mock import Mock
class Foo(object):
    age = 20
    def f1(self):
        return 'this if f1'

    def f2(self, name):
        return name
mock_obj = Mock(spec=Foo)
print(mock_obj.f1)  # <Mock name='mock.f1' id='1847131683640'>
print(mock_obj.f2)  # <Mock name='mock.f2' id='1847131615128'>
print(mock_obj.age)  # <Mock name='mock.age' id='1847131718880'>
print(mock_obj.name)  # AttributeError: Mock object has no attribute 'name'
View Code

為mock對象指定了屬性為Foo類,那么,類中的方法和屬性都是mock對象的屬性,這也是前三個打印沒有問題的原因,而第4個打印報錯了,顯然,Foo類中沒有一個叫name的屬性或者方法。

mock斷言語句

由mock思維導圖知道,mock關於斷言有這些常用的:

  • assert_called_with(arg):檢查函數調用參數是否正確。
  • assert_called_once_with(arg):檢查函數調用參數是否正確,但是只調用一次。
  • assert_any_call():用於檢查測試的mock對象在測試例程中是否調用了方法。
  • assert_has_calls():期望調用方法列表。

assert_called_with assert_called_with檢查mock方法是否獲取了正確的參數,當至少有一個參數有錯誤的值或者類型時、當參數的個數出錯時、當參數的順序不正確時,斷言失敗。

from unittest.mock import Mock

class Foo(object):
    value = 20

    def f1(self, arg):
        return arg

    def f2(self, *args):
        return args

mock_obj = Mock(spec=Foo)
# f1正確的傳參姿勢
mock_obj.f1(222)
# mock_obj.f1.assert_called_with()  # 報錯,沒有傳參
# mock_obj.f1.assert_called_with(11)  # 報錯,瞎98傳參
# mock_obj.f1.assert_called_with('6669')  # 報錯,6翻了吧,傳值的類型不對
# mock_obj.f1.assert_called_with(222)  # 噢啦,mock_obj.f1()傳的就是 222

# f2正確傳參姿勢
mock_obj.f2(1, 2, 3)
# mock_obj.f2.assert_called_with() # 報錯,沒有傳參
# mock_obj.f2.assert_called_with(1)  # 報錯,少傳了參數
# mock_obj.f2.assert_called_with(1, 3, 2)  # 報錯,傳參順序不對
mock_obj.f2.assert_called_with(1, 2, 3)  # 噢啦,傳參姿勢很對
View Code

assert_called_once_with assert_called_once_with斷言,當指定方法被多次調用的時候,斷言失敗。

from unittest.mock import Mock

class Foo(object):
    value = 20

    def f1(self, arg):
        return arg

    def f2(self, *args):
        return args
# 實例化mock對象
mock_obj = Mock(spec=Foo)
# 為f1方法賦返回值
mock_obj.f1.return_value = 222
print(mock_obj.f1())
mock_obj.f1.assert_called_once_with()  # 第一次調用,沒問題
print(mock_obj.f1())
mock_obj.f1.assert_called_once_with()  # 第二次調用,報錯 AssertionError: Expected 'f1' to be called once. Called 2 times.
View Code

這個斷言相對簡單。

assert_any_call assert_any_call斷言用於檢查測試執行中的mock對象在測試中是否調用了方法。

from unittest.mock import Mock

class Foo(object):
    value = 20

    def f1(self, arg):
        return arg

    def f2(self, *args):
        return args


mock_obj = Mock(spec=Foo)
# mock對象調用了 f1() f1(100) f1(200) f1(200)
mock_obj.f1()
mock_obj.f1(100)
mock_obj.f1(200)
mock_obj.f1(200)

# 判斷:mock對象調用了f1() f1(100) f1(200) f1(300) f2()
mock_obj.f1.assert_any_call()  # 沒錯
mock_obj.f1.assert_any_call(100)  # 沒錯
mock_obj.f1.assert_any_call(200)  # 沒錯
# mock_obj.f1.assert_any_call(300)  # AssertionError: f1(300) call not found
mock_obj.f2.assert_any_call()  # AssertionError: f2() call not found
View Code

上例,assert_any_call會判斷整個測試中方法是否被調用了。而不管該方法是否被重復調用。 例如,在程序執行時執行了mock_obj.f1.assert_any_call(),那么就用mock_obj.f1.assert_any_call()判斷剛才的方法是否執行過。執行過啥都不做,要是沒執行過就報錯。

assert_has_calls assert_has_calls檢查是否按照正確的順序和正確的參數進行調用的。所以,需要給出一個方法的調用順序,assert的時候按照這個順序進行檢查。

from unittest.mock import Mock
from unittest.mock import call  # 引入新的模塊

class Foo(object):
    value = 20

    def f1(self, arg):
        return arg

mock_obj = Mock(spec=Foo)
# 正確的執行順序是 f1() f1(100) f1(200)
mock_obj.f1()
mock_obj.f1(100)
mock_obj.f1(200)

# 報錯, 現在的執行順序是 f1() f1(100) f1(300)
# calls_list = [call.f1(), call.f1(100), call.f1(300)]  # 報錯,沒有 call.f1(300)
# mock_obj.assert_has_calls(calls_list)

# 報錯,現在的執行順序是 f1(200) f1() f1(300)
# calls_list = [call.f1(200), call.f1(), call.f1(200)]  # 報錯,執行順序不對
# mock_obj.assert_has_calls(calls_list)

# 對嘍
calls_list = [call.f1(), call.f1(100), call.f1(200)]
mock_obj.assert_has_calls(calls_list)
View Code

首先,以列表的形式列出方法調用順序,每個方法前使用call.f1()的形式,因為如果不加call來修飾的話, 解釋器將不知道f1是一個方法,當然call在使用之前需要引入。

mock管理方法

mock中,關於管理有這些常用方法:

  • attach_mock:將一個mock對象添加到另一個mock對象中。
  • configure_mock,更改mock對象的return_value值。
  • mock_add_spec:給mock對象添加新的屬性。
  • reset_mock:將mock對象恢復到初始狀態。

acttach_mock acttach_mock將一個mock對象添加到另一個mock對象中。

from unittest.mock import Mock


class Foo(object):

    def f1(self, arg):
        return arg


class Bar(object):

    def f2(self, *args):
        pass

# 分別構造foo和bar的mock對象
mock_foo = Mock(spec=Foo)
mock_bar = Mock(spec=Bar)

# 打印也沒問題
print(mock_foo, mock_bar)  # <Mock spec='Foo' id='57738096'> <Mock spec='Bar' id='130627728'>

# 分別為兩個mock對象的方法添加返回值
mock_foo.f1.return_value = 'Foo.f1'
mock_bar.f2.return_value = 'Bar.f2'

# 正常的調用都沒問題
print(mock_foo.f1())  # Foo.f1
print(mock_bar.f2())  # Bar.f2

# 使用attach_mock將mock_bar對象添加到mock_foo中
mock_foo.attach_mock(mock_bar, 'bar')

# 現在mock_bar對象成為了mock_foo mock對象的一個屬性bar
print(mock_foo.bar)  # <Mock name='mock.bar' spec='Bar' id='132987120'>

# mock_foo.bar等於拿到了mock_bar對象,然后調用其中的f2方法,並且得到了之前賦值的返回值
print(mock_foo.bar.f2())  # Bar.f2
View Code

需要注意的是,attach_mock(self, mock, attribute)必須為添加進來的mock對象指定一個屬性名。

configure_mock

configure_mock用來更改mock對象的return_value值。

from unittest.mock import Mock

class Foo(object):

    def f1(self, arg):
        return arg

    def f2(self, arg):
        return arg

# 實例化mock對象並添加屬性和返回值
mock_obj = Mock(spec=Foo, return_value='abc')
# 正常調用mock對象得到預期的結果 abc
print(mock_obj())  # abc
# 使用configure_mock修改mock對象的return_value值
mock_obj.configure_mock(return_value='xyz')
# 修改成功
print(mock_obj())  # xyz

# 可以批量設置返回值,比如f1方法的返回值為 '100', f2方法的返回值為 200
spec_dict = {'f1.return_value': '100', 'f2.return_value': 200}
# 將字典打散后使用configure_mock設置到mock對象中
mock_obj.configure_mock(**spec_dict)
print(mock_obj())  # xyz
print(mock_obj.f1())  # 100  ps:字符串類型的100
print(mock_obj.f2())  # 2090
View Code

mock_add_spec mock_add_spec(self, spec, spec_set=False)用來給mock對象添加一個新的屬性,新的屬性會覆蓋掉原來的屬性。spec_set指屬性可讀可寫,默認是只讀,但可寫我沒測試出來....歡迎留言指正。

from unittest.mock import Mock

class Foo(object):

    def f1(self, arg):
        return arg

class Bar(object):

    def f2(self, args):
        return args

def ace():
    pass

# 實例化mock對象
mock_obj = Mock(spec=Foo)
print(mock_obj.f1())  # <Mock name='mock.f1()' id='119946576'>

# 使用mock_add_spec給mock_obj添加一個新的屬性
mock_obj.mock_add_spec(Bar)
print(mock_obj.f2())  # <Mock name='mock.f2()' id='46952912'>

# 正常的使用都沒問題
mock_obj.f2.return_value = 'Bar.f2'
print(mock_obj.f2())  # Bar.f2

# 上面添加的屬性是類,現在是函數,記得函數這里沒有方法,別瞎點啊
mock_obj.mock_add_spec(ace)
mock_obj.return_value = 'function'
print(mock_obj())  # function

# 另外,新添加的屬性會覆蓋掉之前的屬性。現在的mock對象模擬的函數ace對象,ace函數哪有什么f1啊
print(mock_obj.f1())  # AttributeError: Mock object has no attribute 'f1'
View Code

reset_mock reset_mock將mock對象回復到初識狀態,避免了重新構造mock對象帶來的開銷。

from unittest.mock import Mock

class Foo(object):

    def f1(self, arg):
        return arg

mock_obj = Mock(spec=Foo)
mock_obj.f1()
# 這里如果不使用 reset_mock, 那么f1方法就被調用了兩次,下面的 assert_called_once_with就會報錯,現在則不報錯了
mock_obj.reset_mock()
mock_obj.f1()
mock_obj.f1.assert_called_once_with()
View Code

mock統計方法

再來看mock關於統計的一些方法:

  • called:跟蹤mock對象所做的任意調用的訪問器。
  • mock_calls:顯示工廠調用和方法調用。
  • call_args:mock對象的初始化參數。
  • call_args_list:調用中使用參數。
  • call_count:mock對象被調用次數。
  • method_calls:以列表的形式返回mock對象都調用了哪些方法。

called

from unittest.mock import Mock

def ace():
    pass

# 構造 mock對象並沒有調用
mock_obj = Mock(spec=ace)
# OK,此時mock對象沒有調用,所以mock_obj.called:False
print(mock_obj.called)  # False
# OK,現在調用了,那么 mock_obj.called:True
mock_obj()
print(mock_obj.called)  # True
View Code

called只要檢測到mock對象被調用,就返回True。

call_count

from unittest.mock import Mock

def ace():
    pass

mock_obj = Mock(spec=ace)
mock_obj()
mock_obj()
mock_obj()
print(mock_obj.call_count)  # 3
View Code

call_count檢查mock對象被調用了多少次。

call_args && call_args_list

from unittest.mock import Mock

mock_obj = Mock()
mock_obj()
print(mock_obj.call_args)  # call()
print(mock_obj.call_args_list)  # [call()]
View Code

call_args_list以列表的形式返回工廠調用時所有的參數。

method_calls

from unittest.mock import Mock

class Foo(object):

    def f1(self, arg):
        return arg

mock_obj = Mock(spec=Foo)
mock_obj()
mock_obj.f1()
print(mock_obj.call_args)  # call()
print(mock_obj.call_args_list)  # [call()]
print(mock_obj.method_calls)  # [call.f1()]
View Code

mock_calls

from unittest.mock import Mock

class Foo(object):

    def f1(self, arg):
        return arg

mock_obj = Mock(spec=Foo)
mock_obj()
mock_obj.f1()
print(mock_obj.mock_calls)  # [call(), call.f1()]
View Code

首先,mock對象被調用時,執行工廠call方法,完事第二次調用了f1方法,所以mock_calls返回了兩個方法。


[Mock測試概念介紹](https://www.jianshu.com/p/3944c0b82f30) | [如何 mock 數據](https://www.jianshu.com/p/63056120fab8) | [Python中的模塊學習之mock模塊](https://blog.csdn.net/peiyao456/article/details/77075173)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM