해리의 데브로그

Test Driven Laravel 02 - Book:Update 테스트 코드 구현 / 리팩토링

|

Coder’s tape의 Test Driven Laravel 강의 를 듣고 정리한 포스팅 입니다.

1. Update 테스트 코드 구현

데이터를 업데이트 시키는 기본 코드 작성

/** @test */
public function a_book_can_be_updated()
{
  $this->withoutExceptionHandling();
  $this->post('/books', [
    'title' => 'cool book title',
    'author' => 'harry'
  ]);

  $response = $this->patch('/books', [
    'title' => 'New Title',
    'author' => 'Victor',
  ]);

  $this->assertEquals('New Title', Book::first()->title);
  $this->assertEquals('harry', Book::first()->author);
}

테스트를 동작시키면, 라우트에 대하여 PATCH가 지원되지않는다는 에러 발생. 따라서 라우트를 추가시킴.

//Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException : The PATCH method is not supported for this route. Supported methods: POST.
  
Route::patch('/books', 'BooksController@update');

컨트롤러에 update 메소드 기본 코드 작성.

public function update()
{
  $data = request()->validate([
    'title'=>'required',
    'author'=> 'required',
  ]);
}

하지만, 현재 코드로도 여전히 에러가 발생한다. 업데이트 로직을 구현하기 위해서는 어떤 데이터에 대하여 업데이트를 할 건지를 명시해줘야한다. 이때 데이터의 id값을 사용.

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

  $this->post('/books', [
    'title' => 'cool book title',
    'author' => 'harry'
  ]);

  // book 클래스 인스턴스 추가
  $book = Book::first();  

  //id값을 variable routing으로 사용
  $response = $this->patch('/books/' . $book->id, [ 
    'title' => 'New Title',
    'author' => 'Victor',
  ]);

라우트와 컨트롤러를 적절히 수정시킴.

Route::patch('/books/{book}', 'BooksController@update');
public function update(Book $book)  // route model binding
{
  $data = request()->validate([
    'title'=>'required',
    'author'=> 'required',
  ]);
  
  $book->update($data);
}

2. Create & Update 테스트 코드 및 로직 구현

결과적으로, 데이터를 생성하고 업데이트하는 로직을 성공적으로 아래와 같이 구현 완료

  • 데이터 생성
  • 제목, 작가명 유효성 검증
  • 데이터 업데이트
// BooksController.php
namespace App\Http\Controllers;

use App\Book;
use Illuminate\Http\Request;

class BooksController extends Controller
{
    public function store()
    {
        $data = request()->validate([
            'title'=>'required',
            'author'=> 'required',
        ]);

        Book::create($data);
    }

    public function update(Book $book)
    {
        $data = request()->validate([
            'title'=>'required',
            'author'=> 'required',
        ]);

        $book->update($data);
    }
}
//web.php

Route::post('/books', 'BooksController@store');
Route::patch('/books/{book}', 'BooksController@update');
// BookManagementTest.php
namespace Tests\Feature;

use App\Book;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class BookReservationTest extends TestCase
{
    use refreshDatabase;

    /** @test */
    public function a_book_can_be_added_to_the_library()
    {
        $this->withoutExceptionHandling();
        $response = $this->post('/books', [
            'title' => 'cool book title',
            'author' => 'harry'
        ]);

        $response->assertOk();
        $this->assertCount(1, Book::all());
    }

    /** @test */
    public function a_title_is_required()
    {
        // $this->withoutExceptionHandling();
        $response = $this->post('/books', [
            'title' => '',
            'author' => 'harry'
        ]);

        $response->assertSessionHasErrors('title');
    }

    /** @test */
    public function an_author_is_required()
    {
        $response = $this->post('/books', [
            'title' => 'cool book title',
            'author' => ''
        ]);

        $response->assertSessionHasErrors('author');
    }

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

        $this->post('/books', [
            'title' => 'cool book title',
            'author' => 'harry'
        ]);

        $book = Book::first();

        $response = $this->patch('/books/' . $book->id, [
            'title' => 'New Title',
            'author' => 'victor',
        ]);

        $this->assertEquals('New Title', Book::first()->title);
        $this->assertEquals('victor', Book::first()->author);
    }
}

3. 리팩토링

유효성 검증하는 코드가 반복적으로 사용되고 있음. 이 코드를 extract 시켜 별도의 메소드로 구현

$data = request()->validate([
  'title'=>'required',
  'author'=> 'required',
]);
public function store()
{
  $data = $this->validateRequest();

  Book::create($data);
}

public function update(Book $book)
{
  $data = $this->validateRequest();

  $book->update($data);
}

public function validateRequest()
{
  return request()->validate([
    'title'  => 'required',
    'author' => 'required',
  ]);
}

$data를 인라인으로 변경하는 것도 가능

// before
$data = $this->validateRequest();
$book->update($data);

// after 
$book->update($this->validateRequest());

리팩토링 최종 완성된 코드는 다음과 같음.

class BooksController extends Controller
{
    public function store()
    {
        Book::create($this->validateRequest());
    }

    public function update(Book $book)
    {
        $book->update($this->validateRequest());
    }

    public function validateRequest()
    {
        return request()->validate([
            'title'  => 'required',
            'author' => 'required',
        ]);
    }
}

Comments