簡體   English   中英

如何處理包含太多代碼行的單元/集成測試?

[英]How to handle a unit/integration test that contains too many lines of codes?

在我現在的公司,自 2014 年以來,我們有一個 web 應用程序正在 PHP 由 20 多個不同的開發人員開發。最近,我聽到我的團隊成員抱怨一些單元測試過於龐大(不可讀且不容易跟蹤內部代碼)而不是向測試 class 添加新的場景/方法,他們想創建新的測試類並將這些新場景/方法添加到新的場景/方法中。 假設我們有一個 class,如下所示:

<?php
    class Foo {   
        public function getOne(): string
        public function getAll(): array
        public function validate(): bool
    }
?>

我們有一個現有的單元測試 class 如下:

<?php
    class FooTest {   
        public function testGetOne() {
            // scenario 1
            // scenerio 2
            // scenario 3
            // scenario 4
            // scenario 5
            // scenario 6
            ...
        }
        public function testGetAll() {
            // scenario 1
            // scenario 2
            // scenario 3
            ...
        }
        public function testValidate() {
            // scenario 1
            // scenario 2
            ...
        }
    }
?>

正如您在上面看到的,我們正在遵循一些測試類指南:

  • test class name 組成為原class name + Test postfix
  • 在測試 class 中,為原始 class 的每個可測試方法定義了一個方法(例如testValidate )。
  • 每個測試方法中都列出了測試場景

我們相信它有利於可追溯性。 另一方面,有超過 1000 行的測試類,我們想以某種方式重構它們。 對我來說,要解決的問題是:

  1. 一個class創建多個單元測試類可以嗎? 如果是,那么我們應該根據什么標准將它們分開? (例如,我們是否應該為每個可測試的 function 分別創建一個單元測試 class FooValidateTest ?)
  2. 我們應該如何處理這些測試類的命名約定? 同樣,基於哪些規則?
  3. 我們如何避免為每個單元測試 class 復制/過去多次幫助程序和模擬方法?

我想聽聽你的建議!

  1. 是的,我認為這個選項比不可讀的測試 class 更好。您可以按功能拆分測試,在您的情況下按方法拆分。
  2. 命名測試的規則必須是一個能反映正在測試的功能的名稱。 “GetOneFooTest”可以是名稱的示例。
  3. 功能測試類(“GetOneFootTest”...)從 class“FooTestCase”擴展而來,其中放置了所有 Foo 測試的設置。

給你幾個更多的想法 -

1/ 您可以在擴展的 class 的新層中執行一些設置步驟 - 例如,這是我用來創建一些常規設置的更通用的測試父類之一,以及測試的代碼質量檢查代碼本身:

<?php

namespace Tests;

use PHPUnit\Framework\TestCase as PHPUnit_TestCase;
use PHPUnitGoodPractices\Traits as PHPUnitGoodPractices;

/**
 * @SuppressWarnings(PHPMD.NumberOfChildren)
 */
abstract class TestCase extends PHPUnit_TestCase
{
    use TestCaseTrait;      // setup Mockery
    use \Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
    use PHPUnitGoodPractices\ExpectationViaCodeOverAnnotationTrait;
    use PHPUnitGoodPractices\ExpectOverSetExceptionTrait;
    use PHPUnitGoodPractices\IdentityOverEqualityTrait;
    #use PHPUnitGoodPractices\ProphecyOverMockObjectTrait;
}

我自己的許多測試將擴展Tests\TestCase

正如我在 class 中所使用的那樣,您還可以使用特征將通用功能移出各個測試類。 Soem DocBlock 注釋,例如@before ,也將以與protected function setUp(): void {}等方法相同的方式運行代碼。

2/ @dataProvider可能更有用——它們返回一個數據數組(或從yield Generator方法中產生)。 如果您的測試針對不同數據的多個版本運行相同的測試代碼,則使用 DataProvider 方法允許使用測試方法的數據參數調用單個測試數十次,甚至數百次。

<?php
use PHPUnit\Framework\TestCase;

class DataTest extends TestCase
{
    /**
     * @dataProvider additionProvider
     */
    public function testAdd($a, $b, $expected)
    {
        $this->assertSame($expected, $a + $b);
    }

    public function additionProvider()
    {
        return [
            [0, 0, 0],
            [0, 1, 1],
            [1, 0, 1],
            [1, 1, 3],    // this test would fail
        ];
    }
}

1) - 我會說當你重構你的測試 class 時,你將擁有一個更干凈、更輕量級的 class,例如,我有一個包含 4000 多行代碼的 userTest class。 我構建測試文件夾的方式就是我的項目文件夾的設置方式。 因此,例如,如果我有 App/Repositories/UserRepository,那么我就會有 Tests/Repositories/UserTest。 它更干凈,我寧願不將我的測試分成不同的類。 因為其他開發者永遠不會明白其中的道理。

2) - 我建議使用此命名約定,MethodName_ExpectedBehavior_StateUnderTest, validate_should_throw_an_error_if_X_field_is_missing 或者您可以使用 PascalCase。 https://dzone.com/articles/7-popular-unit-test-naming

3) - 您始終可以將模擬和輔助方法封裝在單獨的 class 中,您可以靜態定義它們或使用測試 class 中的設置方法創建 class 的新實例。

暫無
暫無

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

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