[译]Negative CRUD Unit Testing in Laravel5

栏目: 编程语言 · PHP · 发布时间: 6年前

内容简介:这是一篇译文,原文链接: https://medium.com/@jsdecena/negative-crud-unit-testing-in-laravel-5-7dc728f99fc5作为上一篇我们写的斗士正向测试;断言可以

这是一篇译文,原文链接: https://medium.com/@jsdecena/negative-crud-unit-testing-in-laravel-5-7dc728f99fc5

作为 CRUD Unit Testing in Laravel5 的第二部分,在这篇文章中我们将来讨论反向测试。

上一篇我们写的斗士正向测试;断言可以 createupdateshow 或者 delete Carousel 模型对象,现在让我们进行方向测试,看看如果在执行上面那些动作失败的情况下我们应该如何控制他们?

从create测试开始

<?php
namespace Tests\Unit\Carousels;
use Tests\TestCase;
class CarouselUnitTest extends TestCase
{
    /** @test */
    public function it_should_throw_an_error_when_the_required_columns_are_not_filled()
    {
        $this->expectException(CreateCarouselErrorException::class);
        $carouselRepo = new CarouselRepository(new Carousel);
        $carouselRepo->createCarousel([]);
    }
}
复制代码

还记得吗在创建carousel的migration文件时,我们把 link 字段设置为可空,而 titlesrc 字段设置成了不允许为空。

<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCarouselTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('carousels', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->string('link')->nullable();
            $table->string('src');
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('carousels');
    }
}
复制代码

所以我们预期当尝试设置 titlesrc 为NULL的时候数据库应该抛出一个错误,对吧?好消息是我们在respository类中捕获到了这个错误。

<?php
namespace App\Shop\Carousels\Repositories;
use App\Shop\Carousels\Carousel;
use App\Shop\Carousels\Exceptions\CreateCarouselErrorException;
use Illuminate\Database\QueryException;
class CarouselRepository
{
    /**
     * CarouselRepository constructor.
     * @param Carousel $carousel
     */
    public function __construct(Carousel $carousel)
    {
        $this->model = $carousel;
    }
    /**
     * @param array $data
     * @return Carousel
     * @throws CreateCarouselErrorException
     */
    public function createCarousel(array $data) : Carousel
    {
        try {
            return $this->model->create($data);
        } catch (QueryException $e) {
            throw new CreateCarouselErrorException($e);
        }
    }
}
复制代码

Laravel 中数据库错误会抛出 QueryException 异常,所以我们捕获了这个异常然后创建了一个可读性更高的异常 CreateCarouselErrorException

<?php
namespace App\Shop\Carousels\Exceptions;
class CreateCarouselErrorException extends \Exception
{
}
复制代码

这些准备好后,运行 phpunit 然后看看会发生什么。

PHPUnit 6.5.7 by Sebastian Bergmann and contributors.
.                                                                   1 / 1 (100%)
Time: 993 ms, Memory: 26.00MB
OK (1 test, 1 assertion)
复制代码

上面的结果意味着我们正确地捕获到了这个异常。

之后我们可以在控制器里捕获这个异常并在异常发生时定义我们自己需要的行为。

read test

如果查找不到 Carsouel 模型对象应该怎么办?

<?php
namespace Tests\Unit\Carousels;
use Tests\TestCase;
class CarouselUnitTest extends TestCase
{
    /** @test */
    public function it_should_throw_not_found_error_exception_when_the_carousel_is_not_found()
    {
        $this->expectException(CarouselNotFoundException::class);
        $carouselRepo = new CarouselRepository(new Carousel);
        $carouselRepo->findCarousel(999);
    }
    /** @test */
    public function it_should_throw_an_error_when_the_required_columns_are_not_filled()
    {
        $this->expectException(CreateCarouselErrorException::class);
        $carouselRepo = new CarouselRepository(new Carousel);
        $carouselRepo->createCarousel([]);
    }
}
复制代码

回到repository类中看看我们的 findCarousel() 方法

<?php
namespace App\Shop\Carousels\Repositories;
use App\Shop\Carousels\Carousel;
use App\Shop\Carousels\Exceptions\CarouselNotFoundException;
use App\Shop\Carousels\Exceptions\CreateCarouselErrorException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\QueryException;
class CarouselRepository
{
   protected $model;
  
    /**
     * CarouselRepository constructor.
     * @param Carousel $carousel
     */
    public function __construct(Carousel $carousel)
    {
        $this->model = $carousel;
    }
    ...
      
    /**
     * @param int $id
     * @return Carousel
     * @throws CarouselNotFoundException
     */
    public function findCarousel(int $id) : Carousel
    {
        try {
            return $this->model->findOrFail($id);
        } catch (ModelNotFoundException $e) {
            throw new CarouselNotFoundException($e);
        }
    }
    
     ...
}
复制代码

findCarousel() 方法中我们捕获了Laravel的 findOrFail() 在找不到模型时默认抛出的 ModelNotFoundException

现在再次运行 phpunit

PHPUnit 6.5.7 by Sebastian Bergmann and contributors.
.                                                                   1 / 1 (100%)
Time: 936 ms, Memory: 26.00MB
OK (1 test, 1 assertion)
复制代码

看起来不错,那么如果无法update时我们该怎么办?

update test

<?php
namespace Tests\Unit\Carousels;
use Tests\TestCase;
class CarouselUnitTest extends TestCase
{
    /** @test */
    public function it_should_throw_update_error_exception_when_the_carousel_has_failed_to_update()
    {
        $this->expectException(UpdateCarouselErrorException::class);
        $carousel = factory(Carousel::class)->create();
        $carouselRepo = new CarouselRepository($carousel);
        $data = ['title' => null];
        $carouselRepo->updateCarousel($data);
    }  
  
    /** @test */
    public function it_should_throw_not_found_error_exception_when_the_carousel_is_not_found()
    {
        $this->expectException(CarouselNotFoundException::class);
        $carouselRepo = new CarouselRepository(new Carousel);
        $carouselRepo->findCarousel(999);
    }
    /** @test */
    public function it_should_throw_an_error_when_the_required_columns_are_not_filled()
    {
        $this->expectException(CreateCarouselErrorException::class);
        $carouselRepo = new CarouselRepository(new Carousel);
        $carouselRepo->createCarousel([]);
    }

复制代码

你可以看到,在上面的测试程序里我们有意地将 title 字段设置成了 null, 因为在上一个测试中把 title 设为null在创建 Carousel 时就会抛出错误。所以我们假设数据库的记录中title已经有值了。

Note: 当在测试程序中断言异常时,应该把断言异常的语句放在测试方法的顶部

来看一下repository里的 updateCarousel() 方法

<?php
namespace App\Shop\Carousels\Repositories;
use App\Shop\Carousels\Carousel;
use App\Shop\Carousels\Exceptions\CarouselNotFoundException;
use App\Shop\Carousels\Exceptions\CreateCarouselErrorException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\QueryException;
class CarouselRepository
{
   protected $model;
  
    /**
     * CarouselRepository constructor.
     * @param Carousel $carousel
     */
    public function __construct(Carousel $carousel)
    {
        $this->model = $carousel;
    }
    ...
    
     /**
     * @param array $data
     * @return bool
     * @throws UpdateCarouselErrorException
     */
    public function updateCarousel(array $data) : bool
    {
        try {
            return $this->model->update($data);
        } catch (QueryException $e) {
            throw new UpdateCarouselErrorException($e);
        }
    }
    ...
}
复制代码

运行 phpunit

PHPUnit 6.5.7 by Sebastian Bergmann and contributors.
.                                                                   1 / 1 (100%)
Time: 969 ms, Memory: 26.00MB
OK (1 test, 1 assertion)
复制代码

非常好,大兄弟( 原文:Great dude! :) )

delete test

接下来是 delete 但是我们必须把 deleteCarousel() 方法的返回值类型声明从 bool 改为 ?bool 意思是它可以返回 boolean 或者 null

Note: 你必须运行在 PHP 7.1以上的环境才能应用上面的那个特性http://php.net/manual/en/migration71.new-features.php

<?php
namespace App\Shop\Carousels\Repositories;
use App\Shop\Carousels\Carousel;
use App\Shop\Carousels\Exceptions\CarouselNotFoundException;
use App\Shop\Carousels\Exceptions\CreateCarouselErrorException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\QueryException;
class CarouselRepository
{
   protected $model;
  
    /**
     * CarouselRepository constructor.
     * @param Carousel $carousel
     */
    public function __construct(Carousel $carousel)
    {
        $this->model = $carousel;
    }
    ...
    
    /**
    * @return bool
    */
    public function deleteCarousel() : ?bool
    {
        return $this->model->delete();
    }

复制代码

然后是测试程序

<?php
namespace Tests\Unit\Carousels;
use Tests\TestCase;
class CarouselUnitTest extends TestCase
{
    /** @test */
    public function it_returns_null_when_deleting_a_non_existing_carousel()
    {
        $carouselRepo = new CarouselRepository(new Carousel);
        $delete = $carouselRepo->deleteCarousel();
        $this->assertNull($delete);
    }
    /** @test */
    public function it_should_throw_update_error_exception_when_the_carousel_has_failed_to_update()
    {
        $this->expectException(UpdateCarouselErrorException::class);
        $carousel = factory(Carousel::class)->create();
        $carouselRepo = new CarouselRepository($carousel);
        $data = ['title' => null];
        $carouselRepo->updateCarousel($data);
    }  
  
    /** @test */
    public function it_should_throw_not_found_error_exception_when_the_carousel_is_not_found()
    {
        $this->expectException(CarouselNotFoundException::class);
        $carouselRepo = new CarouselRepository(new Carousel);
        $carouselRepo->findCarousel(999);
    }
    /** @test */
    public function it_should_throw_an_error_when_the_required_columns_are_not_filled()
    {
        $this->expectException(CreateCarouselErrorException::class);
        $carouselRepo = new CarouselRepository(new Carousel);
        $carouselRepo->createCarousel([]);
    }
}
复制代码

运行 phpunit 的结果如下:

➜  git: phpunit --filter=CarouselUnitTest::it_error_when_deleting_a_non_existing_carousel
PHPUnit 6.5.7 by Sebastian Bergmann and contributors.
.                                                                   1 / 1 (100%)
Time: 938 ms, Memory: 26.00MB
OK (1 test, 1 assertion)
复制代码

到这里关于怎么实现CRUD的反向单元测试的过程就讲完了。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Rationality for Mortals

Rationality for Mortals

Gerd Gigerenzer / Oxford University Press, USA / 2008-05-02 / USD 65.00

Gerd Gigerenzer's influential work examines the rationality of individuals not from the perspective of logic or probability, but from the point of view of adaptation to the real world of human behavio......一起来看看 《Rationality for Mortals》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具