簡體   English   中英

帶有繼承的 Python unittest TestCase

[英]Python unittest TestCase with inheritance

目前我有許多類似的單元測試測試用例。 每個測試用例都包含數據(輸入值 + 預期輸出值)和邏輯(調用 SUT 並將實際輸出與預期輸出進行比較)。

我想將數據與邏輯分開。 因此我想要一個只包含邏輯的基類和一個只包含數據的派生類。 到目前為止,我想出了這個:

import unittest

class MyClass():
    def __init__(self, input):
        self.input = input
    def get_result(self):
        return self.input * 2

class TestBase(unittest.TestCase):
    def check(self, input, expected_output):
        obj = self.class_under_test(input)
        actual_output = obj.get_result()
        self.assertEqual(actual_output, expected_output)

    def test_get_result(self):
        for value in self.values:
            self.check(value[0], value[1])

class TestMyClass(TestBase):
    def __init__(self, methodName='runTest'):
        unittest.TestCase.__init__(self, methodName)        
        self.class_under_test = MyClass
        self.values = [(1, 2), (3, 6)]

unittest.main(exit = False)

但這失敗並出現以下錯誤:

AttributeError: 'TestBase' object has no attribute 'values'

兩個問題:

  • 我的“設計”好嗎?
  • 還需要什么才能讓它工作?

這里有點晚,但最近開始需要單元測試繼承

我能找到的最優雅的解決方案是:

首先 - 你需要一個基礎測試類

class MyBaseUnitTest(unittest.TestCase):
    __test__ = False
    def test_someting(self):
        ...

    def test_something_else(self):
        ...

然后繼承該類並運行測試:

class TestA(MyBaseUnitTest):
    __test__ = True

    def test_feature(self):
        pass
    def test_feature2(self):
        pass

這是擁有單個視圖集繼承的最佳且最簡單的方法。

我在多重繼承中發現的問題是,當您嘗試調用setUp()等方法時,它不會在基測試類上調用,因此您必須在您編寫的每個擴展基類的類中調用它。

我希望這會在將來的某個地方幫助某人。

順便說一句:這是在 python3 中完成的 - 我不知道它在 python2 中會如何反應

更新:

這可能更好,更pythonic

class MyBaseUnitTest(object):
    def test_someting(self):
        ...

    def test_something_else(self):
        ...

class TestA(MyBaseUnitTest, unittest.TestCase):

    def test_feature(self):
        pass
    def test_feature2(self):
        pass

只要基礎測試類不擴展“unittest.TestCase”,測試運行器就不會解析這些測試,它們也不會在套件中運行。 它們只會在基類擴展它們的地方運行。

要使這項工作按預期進行,您至少需要:

  • 確保您的子類測試用例的init方法與 TestCase 的匹配,即__init__(self, methodName="runTest")
  • 在子類的init方法中添加一個 super 調用,例如super(TestMyClass, self).__init__(methodName)
  • 給 test_get_result 添加一個 self 參數,即def test_get_result(self):

至於它是否是好的設計,請記住,您的測試在一定程度上充當了您的代碼如何工作的文檔。 如果您將所有工作都隱藏在 TestCase 實例狀態中,那么它的作用就不會那么明顯了。 比方說,編寫一個具有接受輸入和預期輸出的自定義斷言的 mixin 類可能會更好。

設計(或多或少)很好——一個“小問題”是當 unittest 查看所有TestCase 類並運行以“test”開頭的方法時。 此時您有幾個選擇。

一種方法是指定被測和價值類作為屬性。 在這里,如果可能的話,你會希望這些值是不可變的......

class TestBase(unittest.TestCase):

    def check(self, input, expected_output):
        obj = self.class_under_test(input)
        actual_output = obj.get_result()
        self.assertEqual(actual_output, expected_output)

    def check_all(self):
        for value in self.values:
            self.check(value[0], value[1])

class TestMyClass1(TestBase):
    values = ((1, 2), (3, 4))
    class_under_test = MyClass1

    def test_it(self):
        self.check_all()

class TestMyClass2(TestBase):
    values = (('a', 'b'), ('d', 'e'))
    class_under_test = MyClass2

    def test_it(self):
        self.check_all()

你打了兩次test_get_result() 我認為TestBase中根本不應該有任何test*()方法。 相反,使用TestBase提供自定義斷言、錯誤格式化程序、測試數據生成器等,並將實際測試保存在TestMyClass

Python 的unittest.main()執行來自當前命名空間中所有測試類的所有測試。

不直接在基類中運行測試的一種方法是將其移動到不同的模塊(例如將TestBase移動到testbase.py ):

在調用unittest.main()的文件中,確保不要將該類導入您的命名空間(不要使用from testbase import *或類似的):

import testbase

class TestMyClass1(testbase.TestBase):
    values = ((1, 2), (3, 4))
    class_under_test = MyClass1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM