2012-09-13 155 views
24

我使用Jenkins,Python,Selenium2(webdriver)和Py.test框架爲Web測試創建測試用例。Pytest:如果一個失敗了,如何跳過課程中剩下的測試?

到目前爲止,我組織我在下面的結構測試:

每個測試用例每個test_方法測試步驟

當一切正常,但是當一步崩潰時,其餘的「測試步驟」變得瘋狂,這種設置工作得很好。我能夠在teardown_class()的幫助下在類(測試用例)中包含失敗,但是我正在研究如何改善此問題。

我需要的是在某個類中如果其中一個失敗,則跳過(或xfail)其餘的test_方法,以便其他測試用例不會運行並標記爲FAILED(因爲那樣會假陽性)

謝謝!

更新:我沒有看或答案「這是不好的做法」,因爲這樣稱呼它是非常有爭議的。 (每個測試課都是獨立的 - 這應該足夠了)。

UPDATE 2:把「if」條件放在每個測試方法中不是一種選擇 - 是很多重複的工作。我正在尋找的是(也許)有人知道如何使用類方法的hooks

+0

難道你不能只將--maxfail標誌設置爲1?如果一次測試失敗,那麼py.test會結束。 – Nacht

+0

@Nacht的想法是繼續,儘管他們中的一個出現故障測試等測試案例,但停在失敗的測試用例 –

回答

22

我喜歡一般的「測試步驟」的想法。我認爲它是「增量」測試,它在功能測試場景恕我直言,是最有意義的。

這裏是一個不依賴於pytest的內部細節(除了官方鉤擴展)的實現:

import pytest 

def pytest_runtest_makereport(item, call): 
    if "incremental" in item.keywords: 
     if call.excinfo is not None: 
      parent = item.parent 
      parent._previousfailed = item 

def pytest_runtest_setup(item): 
    previousfailed = getattr(item.parent, "_previousfailed", None) 
    if previousfailed is not None: 
     pytest.xfail("previous test failed (%s)" %previousfailed.name) 

如果你現在有一個「test_step.py」是這樣的:

import pytest 

@pytest.mark.incremental 
class TestUserHandling: 
    def test_login(self): 
     pass 
    def test_modification(self): 
     assert 0 
    def test_deletion(self): 
     pass 

然後運行它看起來像這樣(使用-rx對xfail原因報告):

(1)[email protected]:~/p/pytest/doc/en/example/teststep$ py.test -rx 
============================= test session starts ============================== 
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev17 
plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout 
collected 3 items 

test_step.py .Fx 

=================================== FAILURES =================================== 
______________________ TestUserHandling.test_modification ______________________ 

self = <test_step.TestUserHandling instance at 0x1e0d9e0> 

    def test_modification(self): 
>  assert 0 
E  assert 0 

test_step.py:8: AssertionError 
=========================== short test summary info ============================ 
XFAIL test_step.py::TestUserHandling::()::test_deletion 
    reason: previous test failed (test_modification) 
================ 1 failed, 1 passed, 1 xfailed in 0.02 seconds ================= 

我在這裏使用「xfail」,因爲跳過的原因是錯誤的環境或缺少依賴關係,錯誤的解釋器版本。

編輯:請注意,您的示例和我的示例都不能直接與分佈式測試一起使用。爲此,pytest-xdist插件需要增加一種方法來定義要發送到一個測試從屬設備的組/類,而不是通常將類的測試功能發送到不同從屬設備的當前模式。

+0

謝謝Holger。我知道我可能用我的解決方案深入py.test代碼。 :)我一定會嘗試你的建議。另外,我正在爲Web UI自動化創建功能測試用例(最近我一直在講這個問題,因爲我覺得我正在用我的「測試步驟」實現「激起大黃蜂的巢穴」:))爲「pytest-xdist」插件 - 我還沒有嘗試過,但我雖然分發了模塊。無論如何,我可以使用** Jenkins **在奴隸之間分配測試。 –

+0

此外,由於每個*步驟*進一步修改了應用程序的狀態,所以一個普通類拆卸並不是真正的選擇。這就是爲什麼我試圖逐步增加班級「拆解」的原因。 (在這裏http://stackoverflow.com/questions/12538808/pytest-2-3-adding-teardowns-within-the-class/12573949#12573949)我知道你已經發布的答覆有,但我不不認爲它回答了我的問題。 :)我發佈了自己的回覆,你能評論一下嗎?謝謝 –

+0

Holger我剛剛遇到這篇文章,因爲我發現自己需要這個位置!如果這是內置到pytest本身:) –

4

做你在做什麼通常是不好的做法。每個測試應儘可能獨立於其他測試,而您完全依賴於其他測試的結果。

無論如何,閱讀the docs它似乎像一個你想要的功能沒有實現(可能是因爲它沒有被認爲是有用的)。

一個變通可能是「失敗」的測試中調用它設置在類的一些條件的自定義方法,並標記有「SKIPIF」裝飾每一個測試:

class MyTestCase(unittest.TestCase): 
    skip_all = False 

    @pytest.mark.skipIf("MyTestCase.skip_all") 
    def test_A(self): 
     ... 
     if failed: 
      MyTestCase.skip_all = True 
    @pytest.mark.skipIf("MyTestCase.skip_all") 
    def test_B(self): 
     ... 
     if failed: 
      MyTestCase.skip_all = True 

或者你也可以做這個控制器在運行每個測試之前最終會調用pytest.skip()

編輯: 標記爲xfail可以以相同的方式完成,但使用相應的函數調用。

也許不是爲每個測試重寫樣板代碼,而是編寫一個裝飾器(這可能需要你的方法返回一個「標誌」,說明它們是否失敗)。

無論如何,我想指出的是,如您所述,如果其中一個測試失敗,那麼在相同測試用例中的其他失敗測試應被視爲虛假肯定... 但您可以通過「by手」。只需檢查輸出並找出誤報。 即使這可能是無聊的/容易發生的。

+0

任何測試步驟,謝謝您的回答。也許我看到它不同,但是我遇到了一些情況,使測試完全獨立a)沒有意義:例如我測試「用戶」。測試用例包括:1)創建用戶2)修改用戶3)刪除用戶。在「創建用戶」測試用例中,我將不得不做「拆卸」 - 刪除用戶 - 這是另一個測試用例。現在創建「刪除用戶」測試用例沒有任何意義。但是將這兩個測試用例集中在一起並不適合從「測試用例」的角度來看:可能創建成功,但刪除失敗。該怎麼辦? –

+0

b)由於「耗時」的前提條件而無法完成:說一些測試需要pc處於某個「狀態」。這個「狀態」是通過網站控制的,達到這個狀態我需要2-3個小時。之後,我在這個電腦狀態下完成其餘的測試。在這種情況下,我無法將每臺測試用例的PC都置於此狀態 - 完成套件需要幾周的時間。 –

+2

大部分情況都可以通過使用mock來避免。 你不想依賴於「電腦狀況」。你應該創建一個模擬並測試你的代碼在這種情況下的反應。 有一些情況下,如果你想測試程序的運行時內存幾乎滿等,這是不可能的......比如但我認爲,這不應該在所有考慮的單元測試。這些測試控制了全局狀態和程序之間的一些交互。 您可以完全控制全局狀態,或者您根本無法執行_unit_測試。 – Bakuriu

0

更新:請看看@ hpk42答案。他的回答不太侵擾。

這就是我其實是在尋找:

from _pytest.runner import runtestprotocol 
import pytest 
from _pytest.mark import MarkInfo 

def check_call_report(item, nextitem): 
    """ 
    if test method fails then mark the rest of the test methods as 'skip' 
    also if any of the methods is marked as 'pytest.mark.blocker' then 
    interrupt further testing 
    """ 
    reports = runtestprotocol(item, nextitem=nextitem) 
    for report in reports: 
     if report.when == "call": 
      if report.outcome == "failed": 
       for test_method in item.parent._collected[item.parent._collected.index(item):]: 
        test_method._request.applymarker(pytest.mark.skipif("True")) 
        if test_method.keywords.has_key('blocker') and isinstance(test_method.keywords.get('blocker'), MarkInfo): 
         item.session.shouldstop = "blocker issue has failed or was marked for skipping" 
      break 

def pytest_runtest_protocol(item, nextitem): 
# add to the hook 
    item.ihook.pytest_runtest_logstart(
     nodeid=item.nodeid, location=item.location, 
    ) 
    check_call_report(item, nextitem) 
    return True 

現在加入這conftest.py或作爲插件解決我的問題。
此外,如果blocker測試失敗,它也會改進爲停止測試。 (這意味着整個進一步的測試是無用的)

+0

我可以看到像您建議和使用的「測試步驟」組織的用處。雖然這個實現有點不合理,但是:)看看我的例子中的改進。 – hpk42

3
2

你可能想看看pytest-dependency。這是一個插件,如果其他測試失敗,可以跳過一些測試。 在你的情況下,似乎gbonetti討論的增量測試更相關。

相關問題