架構測試

架構測試讓您能夠指定預期來測試應用程式是否符合一組架構規則,協助您維護乾淨且可持續的程式碼庫。預期是由相對名稱空間、完整限定名稱空間或函式名稱來決定的。

以下是定義架構規則範例

1arch()
2 ->expect('App')
3 ->toUseStrictTypes()
4 ->not->toUse(['die', 'dd', 'dump']);
5 
6arch()
7 ->expect('App\Models')
8 ->toBeClasses()
9 ->toExtend('Illuminate\Database\Eloquent\Model')
10 ->toOnlyBeUsedIn('App\Repositories')
11 ->ignoring('App\Models\User');
12 
13arch()
14 ->expect('App\Http')
15 ->toOnlyBeUsedIn('App\Http');
16 
17arch()->preset()->php();
18arch()->preset()->security()->ignoring('md5');

現在,我們深入探討架構測試的各種方法和修改項。在此部分中,您將會學習

  • 預期:允許指定個別的架構規則。
  • 預設:允許使用預定義的個別架構規則組。
  • 修改項:排除或忽略特定類型的檔案、類別、函式或程式碼行。

預期

個別預期讓您能夠為應用程式定義具體的架構規則。以下是可用的預期

toBeAbstract()

toBeAbstract() 方法可用於確保給定名稱空間中的所有類別都是抽象的。

1arch('app')
2 ->expect('App\Models')
3 ->toBeAbstract();

toBeClasses()

toBeClasses() 方法可用於確保給定名稱空間中的所有檔案都是類別。

1arch('app')
2 ->expect('App\Models')
3 ->toBeClasses();

toBeEnums()

toBeEnums() 方法可用於確保給定命名空間中的所有檔案為列舉。

1arch('app')
2 ->expect('App\Enums')
3 ->toBeEnums();

toBeIntBackedEnums()

toBeIntBackedEnums() 方法可用於確保特定命名空間中的所有列舉為整數支援。

1arch('app')
2 ->expect('App\Enums')
3 ->toBeIntBackedEnums();

toBeInterfaces()

toBeInterfaces() 方法可用於確保給定命名空間中的所有檔案為介面。

1arch('app')
2 ->expect('App\Contracts')
3 ->toBeInterfaces();

toBeInvokable()

toBeInvokable() 方法可用於確保給定命名空間中的所有檔案為可調用。

1arch('app')
2 ->expect('App\Actions')
3 ->toBeInvokable();

toBeTraits()

toBeTraits() 方法可用於確保給定命名空間中的所有檔案為特質。

1arch('app')
2 ->expect('App\Concerns')
3 ->toBeTraits();

toBeFinal()

toBeFinal() 方法可用於確保給定命名空間中的所有類別為最終。

1arch('app')
2 ->expect('App\ValueObjects')
3 ->toBeFinal();

請注意,通常這個預期會與 classes() 修改器結合使用,以確保給定命名空間中的所有類別皆為最終。

1arch('app')
2 ->expect('App')
3 ->classes()
4 ->toBeFinal();

toBeReadonly()

toBeReadonly() 方法可用於確保特定類別不可變,且無法在執行階段修改。

1arch('app')
2 ->expect('App\ValueObjects')
3 ->toBeReadonly();

請注意,通常這個預期會與 classes() 修改器結合使用,以確保給定命名空間中的所有類別皆為唯讀。

1arch('app')
2 ->expect('App')
3 ->classes()
4 ->toBeReadonly();

toBeStringBackedEnums()

toBeStringBackedEnums() 方法可用於確保特定命名空間中的所有列舉為字串支援。

1arch('app')
2 ->expect('App\Enums')
3 ->toBeStringBackedEnums();

toBeUsed()

not 修改器與 toBeUsed() 方法結合時,您可以驗證特定類別或函式未被應用程式使用。

1arch('globals')
2 ->expect(['dd', 'dump'])
3 ->not->toBeUsed();
4 
5arch('facades')
6 ->expect('Illuminate\Support\Facades')
7 ->not->toBeUsed();

toBeUsedIn()

透過將 not 修改器與 toBeUsedIn() 方法結合,您可以在給定命名空間中限制特定類別和函式不予使用。

1arch('globals')
2 ->expect('request')
3 ->not->toBeUsedIn('App\Domain');
4 
5arch('globals')
6 ->expect('Illuminate\Http')
7 ->not->toBeUsedIn('App\Domain');

toExtend()

toExtend() 方法可用於確保給定命名空間中的所有類別都擴充特定類別。

1arch('app')
2 ->expect('App\Models')
3 ->toExtend('Illuminate\Database\Eloquent\Model');

toExtendNothing()

toExtendNothing() 方法可用於確保給定命名空間中的所有類別都沒有擴充任何類別。

1arch('app')
2 ->expect('App\ValueObjects')
3 ->toExtendNothing();

toImplement()

toImplement() 方法可用於確保給定命名空間中的所有類別都實作特定介面。

1arch('app')
2 ->expect('App\Jobs')
3 ->toImplement('Illuminate\Contracts\Queue\ShouldQueue');

toImplementNothing()

toImplementNothing() 方法可用於確保給定命名空間中的所有類別都沒有實作任何介面。

1arch('app')
2 ->expect('App\ValueObjects')
3 ->toImplementNothing();

toHaveMethodsDocumented()

toHaveMethodsDocumented() 方法可確認在給定命名空間下的所有方法都有經過文件紀錄。

1arch('app')
2 ->expect('App')
3 ->toHaveMethodsDocumented();

toHavePropertiesDocumented()

toHavePropertiesDocumented() 方法可確認在給定命名空間下的所有屬性都有經過文件紀錄。

1arch('app')
2 ->expect('App')
3 ->toHavePropertiesDocumented();

toHaveAttribute()

toHaveAttribute() 方法可確認給定的類別有特定屬性。

1arch('app')
2 ->expect('App\Console\Commands')
3 ->toHaveAttribute('Symfony\Component\Console\Attribute\AsCommand');

toHaveFileSystemPermissions()

toHaveFileSystemPermissions() 方法可確認在給定命名空間下的所有檔案都有特定的檔案系統權限。

1arch('app')
2 ->expect('App')
3 ->not->toHaveFileSystemPermissions('0777');

toHaveLineCountLessThan()

toHaveLineCountLessThan() 方法可確認在給定命名空間下的所有檔案行數小於指定的值。

1arch('app')
2 ->expect('App\Models')
3 ->toHaveLineCountLessThan(100);

toHaveMethod()

toHaveMethod() 方法可確認給定的類別有特定方法。

1arch('app')
2 ->expect('App\Http\Controllers\HomeController')
3 ->toHaveMethod('index');

toHaveMethods()

toHaveMethods() 方法可確認給定的類別有特定方法。

1arch('app')
2 ->expect('App\Http\Controllers\HomeController')
3 ->toHaveMethods(['index', 'show']);

toHavePrivateMethodsBesides()

toHavePrivateMethodsBesides() 方法可確認給定的類別除了指定的之外,沒有任何私有方法。

1arch('app')
2 ->expect('App\Services\PaymentService')
3 ->not->toHavePrivateMethodsBesides(['doPayment']);

toHavePrivateMethods()

toHavePrivateMethods() 方法可確認給定的類別沒有任何私有方法。

1arch('app')
2 ->expect('App\Services\PaymentService')
3 ->not->toHavePrivateMethods();

toHaveProtectedMethodsBesides()

toHaveProtectedMethodsBesides() 方法可確認給定的類別除了指定的之外,沒有任何受保護的方法。

1arch('app')
2 ->expect('App\Services\PaymentService')
3 ->not->toHaveProtectedMethodsBesides(['doPayment']);

toHaveProtectedMethods()

toHaveProtectedMethods() 方法可確認給定的類別沒有任何受保護的方法。

1arch('app')
2 ->expect('App\Services\PaymentService')
3 ->not->toHaveProtectedMethods();

toHavePublicMethodsBesides()

toHavePublicMethodsBesides() 方法可確認給定的類別除了指定的之外,沒有任何公開的方法。

1arch('app')
2 ->expect('App\Services\PaymentService')
3 ->not->toHavePublicMethodsBesides(['charge', 'refund']);

toHavePublicMethods()

toHavePublicMethods() 方法可確認給定的類別沒有任何公開的方法。

1arch('app')
2 ->expect('App\Services\PaymentService')
3 ->not->toHavePublicMethods();

toHavePrefix()

toHavePrefix() 方法可確認在給定命名空間下的所有檔案都有特定的前綴。

1arch('app')
2 ->expect('App\Helpers')
3 ->not->toHavePrefix('Helper');

toHaveSuffix()

toHaveSuffix() 方法可確認在給定命名空間下的所有檔案都有特定的後綴。

1arch('app')
2 ->expect('App\Http\Controllers')
3 ->toHaveSuffix('Controller');

toHaveConstructor()

toHaveConstructor() 方法可確認在給定命名空間下的所有檔案都有 __construct 方法。

1arch('app')
2 ->expect('App\ValueObjects')
3 ->toHaveConstructor();

toHaveDestructor()

toHaveDestructor() 方法可用於確保給定命名空間內的所有檔案都有 __destruct 方法。

1arch('app')
2 ->expect('App\ValueObjects')
3 ->toHaveDestructor();

toOnlyImplement()

toOnlyImplement() 方法可用於確保特定類別僅限於實作特定介面。

1arch('app')
2 ->expect('App\Responses')
3 ->toOnlyImplement('Illuminate\Contracts\Support\Responsable');

toOnlyUse()

toOnlyUse() 方法可用於保證特定類別僅限於使用特定函式或類別。例如:你可以確保模型是簡潔的,並且僅依賴 Illuminate\Database 命名空間,而不是傳送排程工作或事件。

1arch('models')
2 ->expect('App\Models')
3 ->toOnlyUse('Illuminate\Database');

toOnlyBeUsedIn()

toOnlyBeUsedIn() 方法可讓您將特定類別或類別集的使用範圍限制在應用程式的特定部分。例如:你可以使用此方法確認只有資料儲存庫使用模型,而不是控制器或服務提供者。

1arch('models')
2 ->expect('App\Models')
3 ->toOnlyBeUsedIn('App\Repositories');

toUse()

透過將 not 修飾詞與 toUse() 方法結合使用,你可以表示給定命名空間內的檔案不應使用特定函式或類別。

1arch('globals')
2 ->expect('App\Domain')
3 ->not->toUse('request');
4 
5arch('globals')
6 ->expect('App\Domain')
7 ->not->toUse('Illuminate\Http');

toUseStrictEquality()

toUseStrictEquality() 方法可用於確保給定命名空間內的所有檔案都使用嚴格相等性。換句話說,使用運算子 ===,而不是運算子 ==

1arch('models')
2 ->expect('App')
3 ->toUseStrictEquality();

或者,如果你希望確保給定命名空間內的所有檔案都不使用嚴格相等性,則可以使用 not 修飾詞。

1arch('models')
2 ->expect('App')
3 ->not->toUseStrictEquality();

toUseTrait()

toUseTrait() 方法可用於確保給定命名空間內的所有檔案都使用特定特徵。

1arch('models')
2 ->expect('App\Models')
3 ->toUseTrait('Illuminate\Database\Eloquent\SoftDeletes');

toUseTraits()

toUseTraits() 方法可用於確保給定命名空間內的所有檔案都使用特定特徵。

1arch('models')
2 ->expect('App\Models')
3 ->toUseTraits(['Illuminate\Database\Eloquent\SoftDeletes', 'App\Concerns\CustomTrait']);

toUseNothing()

如果你要表示特定的命名空間或類別不應該有任何依賴關係,可以使用 toUseNothing() 方法。

1arch('value objects')
2 ->expect('App\ValueObjects')
3 ->toUseNothing();

toUseStrictTypes()

toUseStrictTypes() 方法可用於確保給定命名空間內的所有檔案都使用嚴格類型。

1arch('app')
2 ->expect('App')
3 ->toUseStrictTypes();

預設值

有時,從頭開始撰寫「Arch」期望會很耗時,特別是在執行新專案時,你只要確保符合基本的架構規則即可。

預設值是預先定義的一組詳細的期望,你可以使用它們來測試應用程式的架構。

php

php 預設集是一組預先定義的檢測,可用於任何 php 專案。它與任何框架或函式庫都沒有關聯。

它避免使用 dievar_dump 等函式,並確保您不會使用已停用的 PHP 函式。

1arch()->preset()->php();

您可以在我們的 原始程式碼 中找到 php 預設集中包含的所有檢測。

security

security 預設集是一組預先定義的檢測,可用於任何 php 專案。它與任何框架或函式庫都沒有關聯。

它確保您不會使用可能導致安全漏洞的程式碼,例如 evalmd5 等函式。

1arch()->preset()->security();

您可以在我們的 原始程式碼 中找到 security 預設集中包含的所有檢測。

laravel

laravel 預設集是一組預先定義的檢測,可用於 Laravel 專案。

它確保您的專案結構遵循眾所周知的 Laravel 慣例,例如控制器只具有 indexshowcreatestoreeditupdatedestroy 作為公開方法,並且始終附有 Controller 等字尾。

1arch()->preset()->laravel();

您可以在我們的 原始程式碼 中找到 laravel 預設集中包含的所有檢測。

strict

strict 預設集是一組預先定義的檢測,可用於任何 php 專案。它與任何框架或函式庫都沒有關聯。

它確保您在所有檔案中使用嚴格類型,所有類別都是最終類,等等。

1arch()->preset()->strict();

您可以在我們的 原始程式碼 中找到 strict 預設集中包含的所有檢測。

relaxed

relaxed 預設集是一組預先定義的檢測,可用於任何 php 專案。它與任何框架或函式庫都沒有關聯。

它是 strict 預設集的反義詞,確保您不會在所有檔案中使用嚴格類型,所有類別都不是最終類,等等。

1arch()->preset()->relaxed();

您可以在我們的 原始程式碼 中找到 relaxed 預設集中包含的所有檢測。

custom

通常不需要使用 custom 預設集,因為您可以使用 arch() 方法來撰寫精細的檢測。但是,如果您想建立自己的預設集,可以使用 custom 預設集。

如果您有一組檢測在多個專案中頻繁使用,或者如果您是外掛作者並想提供一組檢測給使用者,這可能會很有用。

1pest()->preset('ddd', function () {
2 return [
3 expect('Infrastructure')->toOnlyBeUsedIn('Application'),
4 expect('Domain')->toOnlyBeUsedIn('Application'),
5 ];
6});

preset 方法中,您可以使用封閉式回呼函式的第一個引數存取應用程式 PSR-4 命名空間。

1pest()->preset('silex', function (array $userNamespaces) {
2 dump($userNamespaces); // ['App\\']
3});

修改器

有時候您可能想套用提供的檢測,但排除某些檔案類型,或忽略某些類別、函式或特定的程式碼列。您可以使用下列方法來這麼做

ignoring()

在定義您的架構規則時,您可以使用 ignoring() 方法來排除某些命名空間或類別,否則這些命名空間或類別會包含在規則定義中。

1arch()
2 ->preset()
3 ->php()
4 ->ignoring('die');
5 
6arch()
7 ->expect('Illuminate\Support\Facades')
8 ->not->toBeUsed()
9 ->ignoring('App\Providers');

在某些情況下,某些組件可能不會被視為「相依性」,因為它們是 PHP 本機程式庫的一部分。為了自訂「本機」程式碼的定義並在測試期間將其排除,Pest 允許您指定要忽略的內容。

例如,如果您不希望將 Laravel 視為「相依性」,您可以在 beforeEach() 函數內使用 arch() 方法來忽略「Illuminate」命名空間中的任何程式碼。這種方法允許您只專注於您的應用程式的實際相依性。

1// tests/Pest.php
2pest()->beforeEach(function () {
3 $this->arch()->ignore([
4 'Illuminate',
5 ])->ignoreGlobalFunctions();
6});

classes()

classes() 修改器允許您將預期限制為只有類別。

1arch('app')
2 ->expect('App')
3 ->classes()
4 ->toBeFinal();

enums()

enums() 修改器允許您將預期限制為只有枚舉。

1arch('app')
2 ->expect('App\Models')
3 ->enums()
4 ->toOnlyBeUsedIn('App\Models');

interfaces()

interfaces() 修改器允許您將預期限制為只有介面。

1arch('app')
2 ->expect('App')
3 ->interfaces()
4 ->toExtend('App\Contracts\Contract');

traits()

traits() 修改器允許您將預期限制為只有特徵。

1arch('app')
2 ->expect('App')
3 ->traits()
4 ->toExtend('App\Traits\Trait');

在本節中,您已學習如何執行架構測試,確保您的應用程式或程式庫的架構符合特定的一組架構需求。接下來,您是否曾想過如何測試您的程式碼效能?讓我們來探索 壓力測試