Negative CRUD Unit Testing in Laravel5

这是一篇译文,原文链接: https://medium.com/@jsdecena/...

作为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: 你必须运行在PHP7.1以上的环境才能应用上面的那个特性http://php.net/manual/en/migr...

<?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的反向单元测试的过程就讲完了。

Laravel测试驱动开发--反向单元测试相关推荐

  1. Laravel测试驱动开发--反向单元测试 1

    Negative CRUD Unit Testing in Laravel5 这是一篇译文,原文链接: https://medium.com/@jsdecena/... 作为CRUD Unit Tes ...

  2. Laravel测试驱动开发 -- 正向单元测试

    上一篇翻译的文章里通过简单的十一步讲述了如何在Laravel中进行测试驱动开发,这是作者关于测试的第二篇文章, 文章中简述了如何在Laravel中进行增删改查的单元测试,本文中的单元测试都是正向测试, ...

  3. Laravel测试驱动开发--功能测试

    功能测试 测试驱动开发系列文章的最后一篇文章是关于功能测试的,现在的流行的架构是前后端分离所以很多时候我们并不会像文章里写的那样对响应页面进行功能测试,之前这个系列里的第一篇文章有讲如何对API进行功 ...

  4. Laravel测试驱动开发--功能测试 1

    功能测试 测试驱动开发系列文章的最后一篇文章是关于功能测试的,现在的流行的架构是前后端分离所以很多时候我们并不会像文章里写的那样对响应页面进行功能测试,之前这个系列里的第一篇文章有讲如何对API进行功 ...

  5. rest laravel_如何通过测试驱动开发来构建Laravel REST API

    rest laravel by Kofo Okesola 由Kofo Okesola 如何通过测试驱动开发来构建Laravel REST API (How to build a Laravel RES ...

  6. python测试驱动开发_使用Python进行测试驱动开发的简单介绍

    python测试驱动开发 by Dmitry Rastorguev 德米特里·拉斯托格夫(Dmitry Rastorguev) 使用Python进行测试驱动开发的简单介绍 (A simple intr ...

  7. Java重构与TDD测试驱动开发实际案例一-陈勇-专题视频课程

    Java重构与TDD测试驱动开发实际案例一-2117人已学习 课程介绍         本课程将高深的重构与TDD理论埋藏在一个实际案例中,深入浅出地演示了重构与TDD的完整步骤. 在这个真实的案例中 ...

  8. 用测试驱动开发状态机

    用测试驱动开发状态机 Developing state machines with test-driven development 由于状态机模型在嵌入式系统中的广泛应用,本文探讨了在测试驱动开发(T ...

  9. 测试驱动开发与行为驱动开发中的测试先行方法

    Gil Zilberfeld将在 Agile Practitioners会议上举办小型研讨会,讨论测试先行(test first)方法,测试驱动开发(TDD)和行为驱动开发(BDD)的基础. \\ \ ...

最新文章

  1. ActivityGroup自我堆栈管理(复用现有activity)
  2. Notification 浏览器右下角弹出提示消息
  3. .NET C# 发送邮件内容嵌入图片
  4. 用python计算准确率_Python中计算模型精度的几种方法,Pytorch,中求,准确率
  5. 300英雄服务器维护多久,《300英雄》2021年5月20日6:00-9:00更新维护公告
  6. C#实现像微信PC版一样的扫码登录功能
  7. 如何关闭docker容器里的进程
  8. Scala学习笔记06:自定义控制结构
  9. Blazor编辑表单状态控件
  10. 几乎最全的中文NLP资源库
  11. Android 声音采集回声与回声消除
  12. tailf、tail -f、tail -F三者区别
  13. 固态硬盘计算机怎么自定义分区,固态硬盘分区,详细教您固态硬盘怎么分区
  14. 系统好看 字体font-family
  15. 税收学考试可以带计算机吗,税务师考试能带计算器和草纸吗?简答题需要用笔吗?...
  16. 锋麦4S笔记本英伟达独显驱动安装
  17. 诚之和:百世离场快递恶战“结束的开始”
  18. 吴恩达深度学习笔记六:序列模型
  19. 怎么将pdf转换成excel
  20. 新冠疫情下的化工企业数字化转型

热门文章

  1. 《Cisco交换机配置与管理完全手册》(第二版)前言和目录
  2. Asp.Net MVC使用HtmlHelper渲染,并传递FormCollection参数的陷阱
  3. 如果在Lightning Builder中在标准组件中没有看到Chatter 的情况
  4. EDEN-MACE 1.4.0 更新,增加数据清理功能
  5. Shared Event-loop for Same-Origin Windows(译)
  6. 技术的价值--从实验到企业实施的关键性思想
  7. 注解的原理又是怎么一回事
  8. mac/unix系统:C++实现一个端口扫描器
  9. String类比较,String类运算比较,String运算
  10. 11-13SQLserver基础--数据库之事务