小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

【Laravel系列7.9】測(cè)試

 硬核項(xiàng)目經(jīng)理 2022-06-23 發(fā)布于湖南

測(cè)試

測(cè)試相關(guān)的內(nèi)容其實(shí)也不是我的強(qiáng)項(xiàng),畢竟日常的開發(fā)就沒怎么接觸過,但是不講吧,又總感覺缺少一點(diǎn)什么,所以這一塊也只是簡(jiǎn)單的演示一下,沒辦法帶大家進(jìn)行更加深入的學(xué)習(xí)。

很神奇吧,工作十來年,沒有經(jīng)歷過一個(gè)是使用過測(cè)試驅(qū)動(dòng)開發(fā)的公司,甚至連單元測(cè)試在工作中都從來沒寫過。測(cè)試的好處自然不用多說,道聽途說也了解過測(cè)試驅(qū)動(dòng)開發(fā)的種種好處,也期望在后續(xù)的工作中能夠應(yīng)用上。在這里,也希望大家盡量能找規(guī)模大一些的,開發(fā)比較正規(guī)的公司,類似的這種開發(fā)模式或測(cè)試相關(guān)的知識(shí)也能夠?qū)W習(xí)到更多。

運(yùn)行測(cè)試

Laravel 的測(cè)試組件,主要也是依賴于 PHPUnit 單元測(cè)試組件。這個(gè)東西單獨(dú)拿出來就是可以出一個(gè)系列的。之前我也大致的看過,但是就像上面說過的,并沒有實(shí)際的項(xiàng)目經(jīng)驗(yàn),所以看過也就忘了。如果你對(duì)這一塊有更深入的了解,那么今天的內(nèi)容其實(shí)你也不用再看了。

正因?yàn)槭褂玫氖?PHPUnit ,所以我們可以通過 PHPUnit 來執(zhí)行測(cè)試,比如下面這個(gè)命令。

vendor/bin/phpunit

不過它的報(bào)告格式是原始的 PHPUnit 格式,在 Laravel 框架中,我們更推薦的是使用是框架自帶的一個(gè)測(cè)試命令。

php artisan test

通過跟蹤調(diào)試,我們會(huì)發(fā)現(xiàn)這個(gè)命令的代碼是在 vendor/nunomaduro/collision/src/Adapters/Laravel/Commands/TestCommand.php 文件中,從路徑就可以看出,它不是包含在默認(rèn)的 vendor/laravel 目錄下的。繼續(xù)跟蹤它的 run() 方法,找到 vendor/symfony/process/Process.php 中的 start() 方法。在這個(gè)方法中,如果你是斷點(diǎn)調(diào)試的話,可以看到它會(huì)組合一個(gè)命令行語句,也就是 $commandline 。

public function start(callable $callback = null, array $env = [])
{
    if ($this->isRunning()) {
        throw new RuntimeException('Process is already running.');
    }

    $this->resetProcessData();
    $this->starttime = $this->lastOutputTime = microtime(true);
    $this->callback = $this->buildCallback($callback);
    $this->hasCallback = null !== $callback;
    $descriptors = $this->getDescriptors();

    if ($this->env) {
        $env += $this->env;
    }

    $env += $this->getDefaultEnv();

    if (\is_array($commandline = $this->commandline)) {
        $commandline = implode(' ', array_map([$this'escapeArgument'], $commandline));

        if ('\\' !== \DIRECTORY_SEPARATOR) {
            // exec is mandatory to deal with sending a signal to the process
            $commandline = 'exec '.$commandline;
        }
    } else {
        $commandline = $this->replacePlaceholders($commandline, $env);
    }

    if ('\\' === \DIRECTORY_SEPARATOR) {
        $commandline = $this->prepareWindowsCommandLine($commandline, $env);
    } elseif (!$this->useFileHandles && $this->isSigchildEnabled()) {
        // last exit code is output on the fourth pipe and caught to work around --enable-sigchild
        $descriptors[3] = ['pipe''w'];

        // See https://unix./questions/71205/background-process-pipe-input
        $commandline = '{ ('.$commandline.') <&3 3<&- 3>/dev/null & } 3<&0;';
        $commandline .= 'pid=$!; echo $pid >&3; wait $pid; code=$?; echo $code >&3; exit $code';

        // Workaround for the bug, when PTS functionality is enabled.
        // @see : https://bugs./69442
        $ptsWorkaround = fopen(__FILE__'r');
    }

    $envPairs = [];
    foreach ($env as $k => $v) {
        if (false !== $v) {
            $envPairs[] = $k.'='.$v;
        }
    }

    if (!is_dir($this->cwd)) {
        throw new RuntimeException(sprintf('The provided cwd "%s" does not exist.'$this->cwd));
    }

    $this->process = @proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $this->options);

    if (!\is_resource($this->process)) {
        throw new RuntimeException('Unable to launch a new process.');
    }
    $this->status = self::STATUS_STARTED;

    if (isset($descriptors[3])) {
        $this->fallbackStatus['pid'] = (int) fgets($this->processPipes->pipes[3]);
    }

    if ($this->tty) {
        return;
    }

    $this->updateStatus(false);
    $this->checkTimeout();
}

這個(gè) $commandline 是什么內(nèi)容呢?

'/usr/local/Cellar/php/7.3.9_1/bin/php' 'vendor/phpunit/phpunit/phpunit' '--configuration=/Users/zhangyue/MyDoc/博客文章/LearnLaravel/learn-laravel/phpunit.xml' '--printer=NunoMaduro\Collision\Adapters\Phpunit\Printer'

不驚喜也不意外吧?最終依然使用的是 PHPUnit 的命令行來進(jìn)行自動(dòng)化測(cè)試的。只不過在這里框架幫我們?nèi)フ{(diào)用,并且將執(zhí)行結(jié)果又進(jìn)行了封裝后返回。

好了,現(xiàn)在放開你的斷點(diǎn)繼續(xù)執(zhí)行吧。Laravel 默認(rèn)是帶了一些測(cè)試實(shí)例的,你可以看到一些測(cè)試成功了,一些測(cè)試失敗了。接下來,我們就自己定義一個(gè)測(cè)試。

單元測(cè)試

單元測(cè)試是用于測(cè)試某個(gè)方法的結(jié)果是否符合我們預(yù)期的。在多數(shù)情況下,對(duì)于我們開發(fā)人員來說如果是在測(cè)試開發(fā)驅(qū)動(dòng)的公司,單元測(cè)試肯定是必須要寫的,而且是最重要的測(cè)試內(nèi)容。那么單元測(cè)試一般測(cè)什么呢?不是說隨便一個(gè)方法都要進(jìn)行單元測(cè)試,最需要測(cè)試的其實(shí)是核心業(yè)務(wù)邏輯相關(guān)的一些功能函數(shù)或者類方法。這些比較偏理論了,當(dāng)然也要根據(jù)個(gè)人和公司的情況有所不同,我們也不深究,直接來看看如何使用框架進(jìn)行單元測(cè)試。

首先,我們需要有一個(gè)待測(cè)試的方法,你可以隨便建一個(gè)新類,或者使用已有的類,我這里就使用我們之前建立過的一個(gè) Model ,直接在里面添加一個(gè)方法。

class MTest extends Model
{
    public static function testCulAdd($a, $b){
        return $a+$b;
    }
}

這里只是演示作用哦,不是說只能測(cè)試靜態(tài)方法。這個(gè)方法就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的功能,將兩個(gè)參數(shù)想加。然后我們需要建立一個(gè)測(cè)試類,你可以用下面這個(gè)命令行來實(shí)現(xiàn)。

php artisan make:test CulTest --unit

執(zhí)行命令后,會(huì)在 tests/Unit 目錄下生成一個(gè) CulTest.php 文件。接下來就在這個(gè)文件中寫測(cè)試。

class CulTest extends TestCase
{
    public function test_example()
    
{
        $this->assertEquals(3, MTest::testCulAdd(12));
    }

    public function test_example1()
    
{
        $this->assertEquals(4, MTest::testCulAdd(12));
    }
}

我們定義了兩個(gè)測(cè)試方法,注意要以 test_ 開頭。兩個(gè)方法我們都使用 assertEquals() 斷言,這個(gè)函數(shù)是表示兩個(gè)參數(shù)的結(jié)果相等的話,就返回 true ,也就是測(cè)試的方法的結(jié)果應(yīng)該和我們期望的結(jié)果一致。很明顯就能看出,第一個(gè)測(cè)試方法應(yīng)該是可以通過的,而第二個(gè)方法則可能出現(xiàn)問題。那么我們就來運(yùn)行一下 php artisan test 看看結(jié)果是怎樣的。


結(jié)果符合我們的預(yù)期,整個(gè)測(cè)試實(shí)例是失敗的,那是因?yàn)槠渲幸粋€(gè)測(cè)試方法沒有通過斷言。關(guān)于單元測(cè)試和斷言以及其它相關(guān)的資料,大家可以參考官方文檔或者是 PHPUnit 的文檔,這里就不多說了,繼續(xù)再看看別的測(cè)試方式。

HTTP測(cè)試

HTTP 測(cè)試就是模擬請(qǐng)求,可以幫助我們直接實(shí)現(xiàn)對(duì)頁面或接口的測(cè)試。是不是感覺很強(qiáng)大。創(chuàng)建 HTTP 測(cè)試也可以使用命令行。

php artisan make:test ZyBlogTest

是的,你沒看錯(cuò),和單元測(cè)試類的區(qū)別就是不用加后面那個(gè) --unit 了。也就說,其實(shí) Laravel 框架默認(rèn)是希望我們多使用這種 HTTP 測(cè)試的。好了,我們就來簡(jiǎn)單地測(cè)試一下。

class ZyBlogTest extends TestCase
{
    /**
     * A basic feature test example.
     *
     * @return void
     */

    public function test_example()
    
{
        $response = $this->get('/');

        $response->assertStatus(200);
    }

    public function test_example2()
    
{
        $response = $this->post('/test/post');

        $response->assertStatus(200);
        $response->dump();

    }

    public function test_example3()
    
{
        $response = $this->postJson('/test/post/json');

        $response->assertStatus(200)->assertJson(['a'=>1]);
    }

    public function test_example4()
    
{
        $view = $this->view('test.test', ['message'=>'ZyBlog']);
        $view->assertSee("ZyBlog");
    }
}

第一個(gè)測(cè)試直接測(cè)試的首頁,我們斷言只要返回 200 就可以了。這里直接使用 get() 方法就可以完成 get 請(qǐng)求。第二個(gè)測(cè)試是一個(gè)簡(jiǎn)單的 post 測(cè)試,我們通過 dump() 打印出了 post 輸出的內(nèi)容。當(dāng)然,你也可以使用斷言來判斷測(cè)試內(nèi)容是否符合我們的要求,比如第三個(gè)測(cè)試,我們測(cè)試 json 接口返回的數(shù)據(jù)是否符合要求。在這里,還展示出了鏈?zhǔn)秸{(diào)用的效果。

最后一個(gè)頁面相關(guān)的測(cè)試就比較好玩,你只需要建立一個(gè)下面這樣的頁面。

// resources/views/test/test.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{{$message}}
</body>
</html>

不需要路由,不需要控制器,直接就可以測(cè)試了。測(cè)試的結(jié)果如圖所示。


測(cè)試命令行腳本

命令行測(cè)試就是測(cè)試我們的命令行腳本功能是否正常。在測(cè)試前,我們要先準(zhǔn)備兩個(gè)命令行腳本。簡(jiǎn)單起見,直接使用路由命令行,也就是在 routes/console.php 中直接定義兩個(gè)命令行腳本。這個(gè)東西之前沒有說過,不過這里演示一下大家也就能明白是干什么用的了。

// routes/console.php
Artisan::command('testconsole'function () {
    $this->line("Hello ZyBlog");
});

Artisan::command('question'function () {
    $food = $this->choice('選擇午飯', [
        '面條',
        '蓋飯',
        '火鍋',
    ]);

    $this->line('你的選擇是:'.$food);
});

第一個(gè)命令行,直接輸出一段文字。第二個(gè)則是交互式命令行,會(huì)提示讓你選擇午飯,然后返回你選擇的內(nèi)容。你可以直接運(yùn)行 php artisan testconsole 或者 php artisan question 看看效果。

然后,我們就來寫針對(duì)這兩個(gè)命令行的測(cè)試腳本,你可以繼續(xù)寫在 ZyBlogTest 中。

public function test_console1(){
    $this->artisan('testconsole')->expectsOutput("Hello ZyBlog")->assertExitCode(0);
}

public function test_console2(){
    $this->artisan('question')
        ->expectsQuestion("選擇午飯""面條")
        ->expectsOutput("你的選擇是:面條")
        ->doesntExpectOutput("你的選擇是:蓋飯")
        ->assertExitCode(0);
}

第一條測(cè)試的斷言很簡(jiǎn)單,期望輸出是我們給定的字符串,然后命令行退出碼是 0 就可以了。因?yàn)槲覀儧]有做別的設(shè)置,所以命令行正常退出的退出碼都會(huì)是 0 。

第二個(gè)測(cè)試的斷言就比較復(fù)雜了。我們可以使用 expectsQuestion() 方法模擬選擇輸入的內(nèi)容,然后還是 expectsOutput() 斷言期望的輸出結(jié)果,另外還使用了一個(gè) doesntExpectOutput() 也就是不期望輸出的結(jié)果,通過一系列的組合斷言來決定這個(gè)測(cè)試用例的通過情況。

這個(gè)測(cè)試結(jié)果我就不截圖了,很明顯是正常通過的。大家可以自己修改斷言或者輸出來進(jìn)行更復(fù)雜的測(cè)試。

總結(jié)

通過今天的學(xué)習(xí),我們了解到 Laravel 的測(cè)試組件其實(shí)比我們相像中的要簡(jiǎn)單易用一些。畢竟它幫我們進(jìn)行了封裝,大家只需要去建立測(cè)試類然后寫測(cè)試用例方法就可以了。同時(shí)希望我也能夠和你們一起在實(shí)際的項(xiàng)目工作中用起來。有的時(shí)候并不是公司不需要我們就完全不去管了,這種好東西應(yīng)該是用于約束自己的,即使公司沒要求,我們也可以在自己的代碼中加入這些測(cè)試來提高代碼質(zhì)量,這也是我需要反省的地方。話不多說,大家用起來吧。

參考文檔:

https:///docs/laravel/8.5/testing/10415

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多