qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請(qǐng)?jiān)L問(wèn) http://qaseven.github.io/

          Python單元測(cè)試經(jīng)驗(yàn)總結(jié)

           python寫單元大多數(shù)都會(huì)用到unittest和mock,測(cè)試代碼覆蓋率都會(huì)用到coverage,最后再用nose把所有的東西都串起來(lái),這樣每次出版本,都能把整個(gè)項(xiàng)目的單元測(cè)試都運(yùn)行一遍。
            Unittest
            unittest就不詳細(xì)介紹了,注意幾點(diǎn):
            測(cè)試類繼承unittest.TestCase
            測(cè)試類、測(cè)試方法名字最好以test開(kāi)頭,很多工具能根據(jù)名字來(lái)自動(dòng)運(yùn)行,很方便
            測(cè)試類里面的setUp/tearDown會(huì)在每個(gè)case執(zhí)行之前/之后執(zhí)行,setUpClass/tearDownClass加上@classmethod在整個(gè)測(cè)試類開(kāi)始和結(jié)束的時(shí)候執(zhí)行
            測(cè)試文件的main函數(shù)里面加上unittest.main(),就可以直接用python命令運(yùn)行了
            Mock
            單元測(cè)試?yán)锩姹容^精髓的就是mock了,介紹幾種常見(jiàn)的場(chǎng)景:
            1. Mock一個(gè)函數(shù)。其實(shí)有好幾種方法,個(gè)人比較推薦下面這種,看上去很清晰:
          def multiple(a, b):
          return a*b
          class TestProducer(unittest.TestCase):
          def setUp(self):
          self.calculator = Calculator()
          @mock.patch('multiple')
          def test_multiple(self, mock_multiple):
          mock_multiple.return_value = 3
          self.assertEqual(multiple(8, 14), 3)
            2. Mock一個(gè)對(duì)象里面的方法
          class Calculator(object):
          def add(self, a, b):
          return a+b
          class TestProducer(unittest.TestCase):
          def setUp(self):
          self.calculator = Calculator()
          @mock.patch.object(Calculator, 'add')
          def test_add(self, mock_add):
          mock_add.return_value = 3
          self.assertEqual(self.calculator.add(8, 14), 3)
            3. 讓Mock的函數(shù)每次被調(diào)用返回不同的值,而1,2中的方法每次調(diào)用都會(huì)返回同樣的值
          class TestProducer(unittest.TestCase):
          @mock.patch.object(Calculator, 'add')
          def test_effect(self, mock_add):
          mock_add.side_effect = [1, 2, 3]
          self.assertEqual(self.calculator.add(8, 14), 1)
          self.assertEqual(self.calculator.add(8, 14), 2)
          self.assertEqual(self.calculator.add(8, 14), 3)
            4. 讓Mock的函數(shù)拋出exception
          def is_error(self):
          try:
          os.mkdir("11")
          return False
          except Exception as e:
          return True
          class TestProducer(unittest.TestCase):
          @mock.patch('os.mkdir')
          def test_exception(self, mock_mkdir):
          mock_mkdir.side_effect = Exception
          self.assertEqual(self.calculator.is_error(), True)
            5. Mock多個(gè)函數(shù),主要是注意順序
          @mock.patch.object(Calculator, 'add')
          @mock.patch('test_unit.multiple')
          def test_both(self, mock_multiple, mock_add):
          mock_add.return_value = 1
          mock_multiple.return_value = 2
          self.assertEqual(self.calculator.add(8, 14), 1)
          self.assertEqual(multiple(8, 14), 2)
            Coverage
            打命令coverage加測(cè)試文件,就可以得到覆蓋率,可以生成html格式的報(bào)告,每次運(yùn)行一個(gè)文件都會(huì)生成一個(gè).coverage文件,需要將combine所有結(jié)果才能得到一個(gè)完整的報(bào)告。
            具體的命令參數(shù)參看:http://nedbatchelder.com/code/coverage/cmd.html
            更加有用的是配置文件,參看:http://nedbatchelder.com/code/coverage/config.html
            配置文件中最有用的功能就是可以不測(cè)某些行的覆蓋率,例如:
          [report]
          exclude_lines =
          # 只要在某一行加上注釋“# pragma: no cover”這一行就會(huì)被忽略
          pragma: no cover
          # 忽略掉main函數(shù)
          if __name__ == .__main__.:
            Nose
            Nose可以將所有的單元測(cè)試文件一次全部執(zhí)行,并且提供了coverage的插件,能夠統(tǒng)計(jì)整體的覆蓋率。
            Nose會(huì)掃描目標(biāo)目錄,如果發(fā)現(xiàn)目錄名以“test”或者“Test”開(kāi)頭,則遞歸地進(jìn)去掃描,并自動(dòng)運(yùn)行所有發(fā)現(xiàn)的以“test”或者“Test”開(kāi)頭的測(cè)試文件。
            另外Nose增加了報(bào)級(jí)別的setup和teardown,只需將他們放到__init__.py文件中即可。
            Nose命令的執(zhí)行,最簡(jiǎn)單的就是nosetest后面加上你的所有測(cè)試文件或者測(cè)試文件所在的目錄,一些運(yùn)行參數(shù)參看:http://nose.readthedocs.org/en/latest/usage.html
            Nose的參數(shù)里面以"--cover"開(kāi)頭的都是coverage相關(guān)的,但是我發(fā)現(xiàn)并沒(méi)有辦法是用coverage的配置文件,需要手動(dòng)安裝一下nose-cov,然后用“--cov-config”來(lái)指定配置文件,其他參數(shù)參看:https://pypi.python.org/pypi/nose-cov
            我的項(xiàng)目因?yàn)闇y(cè)試文件比分散,并且有些并沒(méi)有以test開(kāi)頭,所以比較麻煩,只能寫了一個(gè)腳本,把這些都串起來(lái):
          import os
          import subprocess
          ######################################################################
          # 需要測(cè)試覆蓋率的文件或者目錄
          cover_list = [
          'src/sample/analyzer/unpacker/src/emulator.py',
          'src/sample/analyzer/unpacker/src/emulator_manager.py',
          'src/sample/analyzer/unpacker/src/unpacker_analyzer.py',
          'src/sample/analyzer/bitvalue/src/confparser.py',
          'src/sample/analyzer/bitvalue/src/trunk.py',
          ]
          # 測(cè)試用例所在的文件或者目錄,如果測(cè)試文件沒(méi)有以test開(kāi)頭,則必須制定文件名
          ut_list = [
          'src/sample/analyzer/unpacker/ut',
          'src/sample/analyzer/bitvalue/ut/ut_main.py'
          ]
          ######################################################################
          PRODUCTION_HOME = os.environ.get("PRODUCTION_HOME", "../..")
          def get_command():
          command = [
          'nosetests',
          '--with-cov',
          '--cover-erase',
          '--cov-report', 'html',
          '--cov-config', 'cover.config',
          ]
          for cover in cover_list:
          command.append('--cov')
          command.append(os.path.join(PRODUCTION_HOME, cover))
          for ut in ut_list:
          command.append(os.path.join(PRODUCTION_HOME, ut))
          return command
          if __name__ == '__main__':
          command = get_command()
          print command
          os.chdir(PRODUCTION_HOME)
          proc = subprocess.Popen(command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
          output, error = proc.communicate()
          return_code = proc.poll()
          print output
          print error
          print return_code

          posted on 2014-05-27 10:04 順其自然EVO 閱讀(3125) 評(píng)論(1)  編輯  收藏

          評(píng)論

          # re: Python單元測(cè)試經(jīng)驗(yàn)總結(jié) 2014-12-29 17:55 iwalkfreely

          請(qǐng)教一下,coverage獲取覆蓋率時(shí)同時(shí)把測(cè)試文件本身的覆蓋輸出來(lái)了,而我只想要被測(cè)文件的覆蓋率。  回復(fù)  更多評(píng)論   


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          <2014年12月>
          30123456
          78910111213
          14151617181920
          21222324252627
          28293031123
          45678910

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 綦江县| 延寿县| 望奎县| 巫山县| 蕉岭县| 页游| 达州市| 栾川县| 涞水县| 新干县| 资源县| 兰州市| 隆化县| 滁州市| 益阳市| 布拖县| 绿春县| 申扎县| 区。| 当雄县| 高雄县| 桂平市| 铜山县| 吉隆县| 蕲春县| 凤凰县| 潞城市| 腾冲县| 雷州市| 庆阳市| 龙口市| 康平县| 宁南县| 土默特右旗| 合水县| 东乌珠穆沁旗| 同德县| 南平市| 彰化县| 穆棱市| 慈利县|