Laravel 单元测试实战(5)- 再次重构,将数组改为对象
Laravel 单元测试实战(5)- 再次重构,将数组改为对象
github 项目地址
git clone https://gitee.com/three_kingdoms_of_zhang/unit_test_practice.git
composer install
git checkout v6.0
代码是完整的,包括迁移,模型类,和全部功能。
需求
实际上,我们使用 lavarel 的模型类来实现功能,但是硬是用数组去凑,虽然可以,但是不够面向对象,所以想来想去,代码还是改一下,免得误导他人。
再次重构之后的代码
单元测试代码
<?php
namespace Tests\Unit;
use App\Services\Order\ServiceOrder;
use PHPUnit\Framework\TestCase;
class ServiceOrderTest extends TestCase
{
/**
* 二维数组变对象数组。
*
* @param $arr
* @return false|mixed|string
*/
public function json_change($arr)
{
$arr = json_encode($arr, JSON_UNESCAPED_UNICODE);
$arr = json_decode($arr);
return $arr;
}
/**
* 设置购物车
*
* @return array
*/
private function set_datas_to_shopping_carts(): array
{
return [
[
'goods_id' => 1,
'count' => 3,
],
[
'goods_id' => 2,
'count' => 1,
],
[
'goods_id' => 3,
'count' => 2,
],
];
}
/**
* 设置数据库里的商品
*/
private function set_goods_records():array
{
$goods_records = [
[
'id' => 1,
'price' => 10,
],
[
'id' => 2,
'price' => 20,
],
[
'id' => 3,
'price' => 30,
],
];
return $goods_records;
}
/**
* 返回一张满减折扣的优惠券。
*
* @return void
*/
public function test_find_user_coupon_from_shopping_cart_return_manjian_right()
{
$service = new ServiceOrder();
//设置购物车
$shopping_carts = $this->set_datas_to_shopping_carts();
$shopping_carts = $this->json_change($shopping_carts);
// 设置商品数据。 总价 30+ 20 + 60 = 110
$goods_records = $this->set_goods_records();
$goods_records = $this->json_change($goods_records);
// 110的一折是 11元,优惠最大,应该返回id=4记录。
$user_coupon_records = [
[
'id' => 1,
'type' => 1,
'coupon_value' => 5,
'coupon_name' => '优惠券1',
'condition_money' => 1000,
'use_status' =>0,
],
[
'id' => 2,
'type' => 1,
'coupon_value' => 5,
'coupon_name' => '优惠券2',
'condition_money' => 50,
'use_status' =>0,
],
[
'id' => 4,
'type' => 2,
'coupon_value' => 0.9,
'coupon_name' => '优惠券4',
'condition_money' => 50,
'use_status' =>0,
],
[
'id' => 5,
'type' => 1,
'coupon_value' => 6,
'coupon_name' => '优惠券5',
'condition_money' => 50,
'use_status' =>0,
],
];
$user_coupon_records = $this->json_change($user_coupon_records);
$result = $service->find_user_coupon_from_shopping_cart( $shopping_carts, collect($goods_records),
collect($user_coupon_records ) );
$result_expect = [
'saved_money' =>11,
'user_coupon_record'=>(object)[
'id' => 4,
'type' => 2,
'coupon_value' => 0.9,
'condition_money' => 50,
],
];
$this->assertEqualsWithDelta($result['saved_money'], $result_expect['saved_money'],0.02);
$this->assertEquals(($result['user_coupon_record'])->id, ($result_expect['user_coupon_record'])->id);
}
/**
* 返回一张满减固定额的优惠券。
*
* @return void
*/
public function test_find_user_coupon_from_shopping_cart_return_fix_right()
{
$service = new ServiceOrder();
//设置购物车
$shopping_carts = $this->set_datas_to_shopping_carts();
$shopping_carts = $this->json_change($shopping_carts);
// 设置商品数据。 总价 30+ 20 + 60 = 110
$goods_records = $this->set_goods_records();
$goods_records = $this->json_change($goods_records);
// 40元那个,优惠最大,应该返回id=2记录。
$user_coupon_records = [
[
'id' => 1,
'type' => 1,
'coupon_value' => 800,
'coupon_name' => '优惠券1',
'condition_money' => 1000,
'use_status' =>0,
],
[
'id' => 2,
'type' => 1,
'coupon_value' => 40,
'coupon_name' => '优惠券2',
'condition_money' => 50,
'use_status' =>0,
],
[
'id' => 4,
'type' => 2,
'coupon_value' => 0.9,
'coupon_name' => '优惠券4',
'condition_money' => 50,
'use_status' =>0,
],
[
'id' => 5,
'type' => 1,
'coupon_value' => 6,
'coupon_name' => '优惠券5',
'condition_money' => 50,
'use_status' =>0,
],
];
$user_coupon_records = $this->json_change($user_coupon_records);
$result = $service->find_user_coupon_from_shopping_cart( $shopping_carts, collect($goods_records),
collect($user_coupon_records ) );
$result_expect = [
'saved_money' =>40,
'user_coupon_record'=>(object)[
'id' => 2,
'type' => 1,
'coupon_value' =>40,
'condition_money' => 50,
],
];
$this->assertEqualsWithDelta($result['saved_money'], $result_expect['saved_money'],0.02);
$this->assertEquals(($result['user_coupon_record'])->id, ($result_expect['user_coupon_record'])->id);
}
/**
* 用户没有优惠券的情况。
*
* @return void
*/
public function test_find_user_coupon_from_shopping_cart_when_no_coupon_return_0()
{
$service = new ServiceOrder();
//设置购物车
$shopping_carts = $this->set_datas_to_shopping_carts();
$shopping_carts = $this->json_change($shopping_carts);
// 设置商品数据。 总价 30+ 20 + 60 = 110
$goods_records = $this->set_goods_records();
$goods_records = $this->json_change($goods_records);
// 110的一折是 11元,优惠最大,应该返回id=4记录。
$user_coupon_records = [];
$user_coupon_records = $this->json_change($user_coupon_records);
$result = $service->find_user_coupon_from_shopping_cart( $shopping_carts, collect($goods_records),
collect($user_coupon_records ) );
$result_expect = [
'saved_money' =>0,
'user_coupon_record'=>null
];
$this->assertEquals($result['saved_money'], $result_expect['saved_money']);
$this->assertNull($result['user_coupon_record']);
}
/**
* 用户有优惠券的情况,但没有满足限制条件总价,所以应该一张优惠券都不给。
*
* @return void
*/
public function test_find_user_coupon_from_shopping_cart_when_no_coupon_meeting_the_conditions_return_0()
{
$service = new ServiceOrder();
//设置购物车
$shopping_carts = $this->set_datas_to_shopping_carts();
$shopping_carts = $this->json_change($shopping_carts);
// 设置商品数据。 总价 30+ 20 + 60 = 110
$goods_records = $this->set_goods_records();
$goods_records = $this->json_change($goods_records);
// 用户有优惠券,但条件太高,满足不了。
$user_coupon_records = [
[
'id' => 1,
'type' => 1,
'coupon_value' => 5,
'coupon_name' => '优惠券1',
'condition_money' => 1000,
'use_status' =>0,
],
[
'id' => 2,
'type' => 1,
'coupon_value' => 5,
'coupon_name' => '优惠券2',
'condition_money' => 2000,
'use_status' =>0,
],
];
$user_coupon_records = $this->json_change($user_coupon_records);
$result = $service->find_user_coupon_from_shopping_cart( $shopping_carts, collect($goods_records),
collect($user_coupon_records ) );
$result_expect = [
'saved_money' =>0,
'user_coupon_record'=>null
];
$this->assertEquals($result['saved_money'], $result_expect['saved_money']);
$this->assertNull($result['user_coupon_record']);
}
}
集成测试代码
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\DatabaseTestCase;
use Tests\TestCase;
class OrderControllerTest extends DatabaseTestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$response = $this->postJson('/api/find_user_coupon', [
'user_id' =>1,
'shopping_cart'=>[
[
'goods_id' => 1,
'count' => 3,
],
[
'goods_id' => 2,
'count' => 1,
],
[
'goods_id' => 3,
'count' => 2,
],
],
]);
$response->assertStatus(200);
$data = $response->getContent();
$data = json_decode($data,1);
$result = $data['data'];
$result_expect = [
'saved_money' =>11,
'user_coupon_record'=>[
'id' => 4,
'type' => 2,
'coupon_value' => 0.9,
'condition_money' => 50,
],
];
$this->assertEqualsWithDelta($result['saved_money'], $result_expect['saved_money'],0.02);
$this->assertEquals($result['user_coupon_record']['id'], $result_expect['user_coupon_record']['id']);
}
public function get_table_datas()
{
return <<<sql
goods:
-
id: 1
goods_name: "商品1"
price: 10
-
id: 2
goods_name: "商品2"
price: 20
-
id: 3
goods_name: "商品3"
price: 30
user_coupons:
-
id: 1
type: 1
coupon_value: 5
coupon_name: "优惠券1"
condition_money: 1000
use_status: 0
user_id: 1
-
id: 2
type: 1
coupon_value: 5
coupon_name: "优惠券2"
condition_money: 50
use_status: 0
user_id: 1
-
id: 4
type: 2
coupon_value: 0.9
coupon_name: "优惠券4"
condition_money: 50
use_status: 0
user_id: 1
-
id: 5
type: 1
coupon_value: 6
coupon_name: "优惠券5"
condition_money: 50
use_status: 0
user_id: 1
sql;
}
}
购物车对象
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2022/9/23
* Time: 13:14
*/
namespace App\Services\Order;
use Exception;
/**
* 购物车参数对象,与数据库无关。
*
* Class ShoppingCart
* @package App\Services\Order
*/
class ShoppingCart
{
public $goods_id;
public $count;
public function __construct($goods_id, $count)
{
if (!$goods_id) {
throw new Exception('入参商品id错误');
}
$count = intval($count);
if (!$count) {
throw new Exception('入参商品数量错误');
}
$this->goods_id=$goods_id;
$this->count=$count;
}
}
控制器
<?php
namespace App\Http\Controllers\Order;
use App\Http\Controllers\Controller;
use App\Services\Order\ServiceOrder;
use App\Services\Order\ShoppingCart;
use App\Services\Stub;
use Illuminate\Http\Request;
class OrderController extends Controller
{
/**
* 查找优惠券。
*
* @param Request $request
* @param ServiceOrder $serviceOrder
* @return \Illuminate\Http\JsonResponse
*/
public function find_user_coupon(Request $request, ServiceOrder $serviceOrder)
{
$input_user_id = $request->input('user_id');
$input_shopping_carts = $request->input('shopping_cart');
$shopping_carts = [];
foreach ($input_shopping_carts as $input_shopping_cart) {
$shopping_carts []= new ShoppingCart($input_shopping_cart['goods_id'], $input_shopping_cart['count'] );
}
$user_coupon_record = $serviceOrder->controller_find_user_coupon($input_user_id, $shopping_carts);
$data =[
'code' =>0,
'data' => $user_coupon_record,
];
return response()->json($data)->setEncodingOptions(JSON_UNESCAPED_UNICODE);
}
}
核心 service
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2022/9/23
* Time: 13:14
*/
namespace App\Services\Order;
use App\Models\Goods;
use App\Models\UserCoupon;
use Illuminate\Support\Collection;
class ServiceOrder
{
/*
* 查找优惠券。
*
* @param $input_user_id
* @param $input_shopping_carts array 购物车格式
* [
* [
* 'goods_id' => 1,
* 'count' => 3,
* ],
* ... ...
* ];
*
* @throws \Exception
*/
public function controller_find_user_coupon($input_user_id,$shopping_carts )
{
// 用户持有的优惠券。
$user_coupon_records = UserCoupon::query()
->where('user_id', $input_user_id)
->where('use_status', 0)
->get();
//根据购物车查商品。
$goods_records =collect([]);
foreach ( $shopping_carts as $shopping_cart ){
$goods_records->push( Goods::findOrFail($shopping_cart->goods_id) );
}
//根据购物车内容,及查出的商品价格,还有用户已有的优惠券。去查找最优的优惠券。
$user_coupon_record = $this->find_user_coupon_from_shopping_cart(
$shopping_carts, $goods_records ,$user_coupon_records
);
return $user_coupon_record;
}
/*
* 根据购物车信息,和已有的优惠券,查找最优惠的一张优惠券。
*
* @param array $shopping_cart 前端传来的商品信息。
* // 购物车格式
* $shopping_cart = [
* [
* 'goods_id' => 1,
* 'count' => 3,
* ],
* ... ...
* ];
*
* @param Collection $goods_records 自己查的数据库信息,商品的。
* @param Collection $user_coupon_records 自己查的用户优惠券信息。
*
* @return [
* 'saved_money' => 0, //优惠券自身的等价金额
* 'user_coupon_record' => [ //优惠券记录,或者null
* 'id' => 4,
* 'type' => 2,
* 'coupon_value' => 0.9,
* 'condition_money' => 50,
* ]
* ]
*/
public function find_user_coupon_from_shopping_cart(array $shopping_carts,Collection $goods_records,
Collection $user_coupon_records):array
{
$total = $this->calculate_total_price( $shopping_carts, $goods_records );
$finded_user_coupon = $user_coupon_records
->filter(function ($user_coupon)use($total){
return $user_coupon->condition_money < $total;
})->map(function($user_coupon)use($total){
if ($user_coupon->type==1) {
$user_coupon->_saved_money = $user_coupon->coupon_value ;
}
if ($user_coupon->type==2) {
$user_coupon->_saved_money = $total * (1- $user_coupon->coupon_value) ;
}
return $user_coupon;
})->sortByDesc(function ($user_coupon) {
return $user_coupon->_saved_money;
})->first();
if ($finded_user_coupon) {
return [
'saved_money' => $finded_user_coupon->_saved_money, //优惠券自身的等价金额
'user_coupon_record' => $finded_user_coupon,
];
}
return [
'saved_money' => 0,
'user_coupon_record' => null,
];
}
/**
* 计算购物车总价。
*
* @param array $shopping_cart
* @param Collection $goods_records
* @return float
* @throws \Exception
*/
private function calculate_total_price(array $shopping_carts,Collection $goods_records):float
{
$total=0;
foreach( $shopping_carts as $shopping_cart){
$count = $shopping_cart->count;
$unit_price=0;
foreach ( $goods_records as $goods ){
if ( $goods->id == $shopping_cart->goods_id ){
$unit_price = $goods->price;
break;
}
}
if (!$unit_price) {
throw new \Exception('参数错误');
}
$total += $count * $unit_price;
}
return $total;
}
}
总结:
1、假设了4种获得优惠券的情况,都放在速度极快的单元测试代码中完成,耗时的集成测试只放了一种情况,十分完美。
本作品采用《CC 协议》,转载必须注明作者和本文链接