php 除了单元测试,我还能有更多的功能测试吗

iswrvxsc  于 2022-11-21  发布在  PHP
关注(0)|答案(1)|浏览(116)

我一直在阅读有关测试的文章,我发现一些文章说我应该有更多的单元测试而不是功能测试;一个叫做测试金字塔的东西。
我已经使用TDD启动了一个简单的系统,到目前为止,我基本上已经编写了特性测试(下面的例子),因为我知道它们是最重要的,因为它们与用户真正的行为(比如发出帖子请求)更相关(点击一个按钮,这样做),并收到一个回应。但因为我读到的,我现在不确定是应该继续这样做,还是应该创建单元测试来测试我在控制器中调用的服务方法。
即使为我的每个服务方法编写了单元测试,我仍然会有比单元测试更多或相等的功能测试,因为我的服务的每个方法都与一个端点相关,用户可以向它发送不同的数据。
另外,如果我理解测试错误,请随时纠正我.谢谢
测试项目:

<?php

namespace Tests\Feature\Cms;

use App\Models\Group;
use App\Models\Modules;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class UsersTest extends TestCase
{

  use WithFaker, RefreshDatabase;

  /** @test */
  public function unauthenticated_users_are_redirected()
  {
    $this->withoutExceptionHandling();

    $response = $this->get(route('cms.users.index'));

    $response->assertRedirect();
  }

  /** @test */
  public function a_user_can_be_created()
  {
    $this->withoutExceptionHandling()->signIn();

    $password = $this->faker->password(6, 12);
    $user_data = [
      'email' => $this->faker->safeEmail(),
      'fk_group_id' => Group::factory()->has(Modules::factory(), 'modules')->create()->id,
      'name' => $this->faker->name(),
      'password' => $password,
      'password_confirmation' => $password,
    ];

    $response = $this->post(route('cms.users.store'), $user_data);

    $response->assertSessionHas('response', cms_response(trans('cms.users.success_create')));
  }

  /** @test */
  public function if_passwords_dont_match_when_creating_a_user_an_error_is_returned()
  {
    $this->signIn();

    $user_data = [
      'email' => $this->faker->safeEmail(),
      'fk_group_id' => Group::factory()->has(Modules::factory(), 'modules')->create()->id,
      'name' => $this->faker->name(),
      'password' => $this->faker->password(6, 12),
      'password_confirmation' => $this->faker->password(6, 12),
    ];

    $response = $this->post(route('cms.users.store'), $user_data);

    $this->checkIfSessionErrorMatchesString('password', 'A senha e confirmação de senha não são iguais.');
    $this->checkIfSessionErrorMatchesString('password_confirmation', 'A senha e confirmação de senha não são iguais.');
  }

  /** @test */
  public function a_user_can_be_updated()
  {
    $this->withoutExceptionHandling()->signIn();

    $user = User::factory()->withPassword($this->faker->password(6, 12))->create();
    $password = $this->faker->password(6, 12);
    $user_data = [
      'email' => $this->faker->safeEmail(),
      'fk_group_id' => Group::factory()->has(Modules::factory(), 'modules')->create()->id,
      'name' => $this->faker->name(),
      'password' => $password,
      'password_confirmation' => $password,
    ];

    $response = $this->patch(route('cms.users.update', ['user' => $user->id]), $user_data);

    $response->assertSessionHas('response', cms_response(trans('cms.users.success_update')));
  }

  /** @test */
  public function if_the_user_isnt_found_an_error_is_returned()
  {
    $this->withoutExceptionHandling()->signIn();

    $user = User::all()->last();
    $password = $this->faker->password(6, 12);
    $user_data = [
      'email' => $this->faker->safeEmail(),
      'fk_group_id' => Group::factory()->has(Modules::factory(), 'modules')->create()->id,
      'name' => $this->faker->name(),
      'password' => $password,
      'password_confirmation' => $password,
    ];

    $response = $this->patch(route('cms.users.update', ['user' => $user->id + 1]), $user_data);

    $response->assertSessionHas('response', cms_response(trans('cms.users.error_user_not_found'), false, 400));
  }

  /** @test */
  public function a_user_can_be_updated_without_updating_its_password()
  {
    $this->withoutExceptionHandling()->signIn();

    $user = User::factory()->withPassword($this->faker->password(6, 12))->create();
    $user_data = [
      'email' => $this->faker->safeEmail(),
      'fk_group_id' => Group::factory()->has(Modules::factory(), 'modules')->create()->id,
      'name' => $this->faker->name(),
    ];

    $response = $this->patch(route('cms.users.update', ['user' => $user->id]), $user_data);

    $response->assertSessionHas('response', cms_response(trans('cms.users.success_update')));
  }

  /** @test */
  public function if_passwords_dont_match_when_updating_a_user_an_error_is_returned()
  {
    $this->signIn();

    $user = User::factory()->withPassword($this->faker->password(6, 12))->create();
    $user_data = [
      'email' => $this->faker->safeEmail(),
      'fk_group_id' => Group::factory()->has(Modules::factory(), 'modules')->create()->id,
      'name' => $this->faker->name(),
      'password' => $this->faker->password(6, 12),
      'password_confirmation' => $this->faker->password(6, 12),
    ];

    $this->patch(route('cms.users.update', ['user' => $user->id]), $user_data);

    $this->checkIfSessionErrorMatchesString('password', 'A senha e confirmação de senha não são iguais.');
    $this->checkIfSessionErrorMatchesString('password_confirmation', 'A senha e confirmação de senha não são iguais.');
  }

  /** @test */
  public function users_can_be_excluded()
  {
    $this->withoutExceptionHandling()->signIn();

    $users_id = User::factory(2)->withPassword($this->faker->password(6, 12))->create()->pluck('id');

    $response = $this->delete(route('cms.users.destroy', $users_id));

    $response->assertSessionHas('response', cms_response(trans('cms.users.success_delete')));
  }
}

控制器:

<?php

namespace App\Http\Controllers\Cms;

use App\Http\Controllers\Controller;
use App\Http\Requests\UserRequest;
use App\Services\CmsUsersService;
use Illuminate\Http\Request;

class UsersController extends Controller
{
  private $service;

  public function __construct(CmsUsersService $service)
  {
    $this->service = $service;
  }

  public function store(UserRequest $request)
  {
    $result = $this->service->create($request->all());
    return redirect()->back()->with('response', $result);
  }

  public function update(UserRequest $request, $id)
  {
    $result = $this->service->update($id, $request->all());
    return redirect()->back()->with('response', $result);
  }

  public function destroy($users_id)
  {
    $result = $this->service->delete($users_id);
    return redirect()->back()->with('response', $result);
  }
}

服务项目:

<?php

namespace App\Services;

use App\Models\User;
use Exception;
use Illuminate\Support\Facades\Hash;
use App\Interfaces\CRUD;

class CmsUsersService implements CRUD
{
  public function create(array $data)
  {
    $data['token'] = Hash::make($data['email']);
    $data['password'] = Hash::make($data['password']);
    User::create($data);

    return cms_response(trans('cms.users.success_create'));
  }

  public function update(int $id, array $data)
  {
    try {
      if (array_key_exists('password', $data)) {
        $data['password'] = Hash::make($data['password']);
      }

      $user = $this->__findOrFail($id);
      $user->update($data);

      return cms_response(trans('cms.users.success_update'));
    } catch (\Throwable $th) {
      return cms_response($th->getMessage(), false, 400);
    }
  }

  public function delete(string $ids)
  {
    User::whereIn('id', json_decode($ids))->delete();
    return cms_response(trans('cms.users.success_delete'));
  }

  private function __findOrFail(int $id)
  {
    $user = User::find($id);
    if ($user instanceof User) {
      return $user;
    }
    throw new Exception(trans('cms.users.error_user_not_found'));
  }
}
k4ymrczo

k4ymrczo1#

我从实践和Jeffrey Way中学到,你主要做的是特性测试,而不是特别担心单元测试。直到那时,当你不确定底层的类方法不会像预期的那样工作时;您可以深入到单元级别并确保该方法也能处理边缘情况。

相关问题