内容简介:这是一篇译文,原文链接: 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 的第二部分,在这篇文章中我们将来讨论反向测试。
上一篇我们写的斗士正向测试;断言可以 create
、 update
、 show
或者 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
字段设置为可空,而 title
和 src
字段设置成了不允许为空。
<?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'); } } 复制代码
所以我们预期当尝试设置 title
和 src
为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
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》 这本书的介绍吧!