테스트

프로바이더 테스트

서비스 컨테이너에 바인딩된 서비스를 테스트할 때 사용할 수 있는 몇 가지 방법을 설명하겠습니다. 이를 통해 바인딩된 서비스의 동작을 검증하고, 의존성을 주입받는 클래스의 테스트를 용이하게 할 수 있습니다.

1. 모의 객체(Mock) 사용

라라벨에서는 PHPUnit과 Mockery를 사용하여 모의 객체를 생성하고 테스트할 수 있습니다. 모의 객체를 통해 실제 구현체 대신 가짜 객체를 주입하여 테스트를 수행합니다.

예시 코드

먼저, 테스트할 인터페이스와 서비스를 정의합니다.

namespace App\Services;

interface GreetingServiceInterface
{
    public function greet($name);
}

class GreetingService implements GreetingServiceInterface
{
    public function greet($name)
    {
        return "Hello, $name!";
    }
}

그 다음, 컨트롤러를 정의합니다.

namespace App\Http\Controllers;

use App\Services\GreetingServiceInterface;

class GreetingController extends Controller
{
    protected $greetingService;

    public function __construct(GreetingServiceInterface $greetingService)
    {
        $this->greetingService = $greetingService;
    }

    public function greet($name)
    {
        return $this->greetingService->greet($name);
    }
}

테스트 클래스를 생성하고 모의 객체를 사용하여 테스트합니다.

use Tests\TestCase;
use App\Services\GreetingServiceInterface;
use App\Http\Controllers\GreetingController;
use Mockery;

class GreetingControllerTest extends TestCase
{
    public function testGreet()
    {
        // GreetingServiceInterface의 모의 객체 생성
        $mockService = Mockery::mock(GreetingServiceInterface::class);

        // greet 메서드가 "Hello, John!"을 반환하도록 설정
        $mockService->shouldReceive('greet')
                    ->with('John')
                    ->andReturn('Hello, John!');

        // 컨트롤러에 모의 객체 주입
        $controller = new GreetingController($mockService);

        // 메서드 호출 및 결과 검증
        $response = $controller->greet('John');
        $this->assertEquals('Hello, John!', $response);
    }

    protected function tearDown(): void
    {
        Mockery::close();
        parent::tearDown();
    }
}

2. 서비스 컨테이너를 통한 테스트

라라벨의 서비스 컨테이너를 이용하여 직접 인스턴스를 주입하는 방식으로 테스트할 수 있습니다.

예시 코드

먼저, 서비스를 서비스 컨테이너에 바인딩합니다.

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\GreetingServiceInterface;
use App\Services\GreetingService;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->bind(GreetingServiceInterface::class, GreetingService::class);
    }

    public function boot()
    {
        //
    }
}

그 다음, 테스트 클래스를 작성합니다.

use Tests\TestCase;
use App\Services\GreetingServiceInterface;

class GreetingServiceTest extends TestCase
{
    public function testGreet()
    {
        // 서비스 컨테이너에서 인스턴스 가져오기
        $greetingService = $this->app->make(GreetingServiceInterface::class);

        // 메서드 호출 및 결과 검증
        $response = $greetingService->greet('John');
        $this->assertEquals('Hello, John!', $response);
    }
}

요약

Q1: Mock 객체와 실제 객체를 사용하는 테스트의 장단점은 무엇인가요?

Q2: 테스트 시 의존성 주입의 역할과 중요성은 무엇인가요?

Q3: 라라벨에서 더 복잡한 서비스나 클래스를 테스트할 때 어떤 전략을 사용할 수 있나요?

You wanna more detailed information?

모킹

https://darkghosthunter.medium.com/php-10-tips-to-use-for-mockery-33673ba01321

  1. 정적 메서드는 피해라

테스트를 할 때마다 문제가 발생할 수 있는 가능성을 만든다

정적 속성에는 수 많은 문제가 있으며 테스트 가능성도 그 중 하나다. 따라서 정적 속성을 상수처럼 취급하거나, 전체 앱 라이플 사이클에서 한번만 설정해야 하는 값이거나 테스트 내부에서만 독점적으로 취급하지 않는 한 사용하지 말자

다른 방법이 없다면 가능한 한 객체의 원래 상태를 항상 재설정 하자

  1. 구문을 더 짧게 해라

  2. 몇 년 전, Mockery는 테스트 코드에서 유용하지 않은 일부 줄을 제거할 수 있는 expects()및 메서드를 구현했습니다.allows()

기존 구문과 비교했을 때, 새로운 현대 구문은 키보드 입력을 줄여주고 더 이해하기 쉽게 만들어줍니다.

존경받는 개발자라면 모든 메서드에 대해 객체를 모의하고 항상 자체를 반환하라고 말할 것입니다. 이는 메서드 수만큼만 증가하므로 수십 개가 있으면 꽤 지저분해질 수 있습니다.

대신 메서드의 인수를 무시하고도 올바른 순서로 호출되도록 하려는 경우 -> 을 사용하여 모든 메서드를 한 번에 연결할 수 있습니다

. 주문방법 어떤 객체에서는 다른 메소드를 호출한 후에 다른 메소드를 호출하면 역순으로 호출한 경우와 다른 결과가 나옵니다. 예를 들어, 뺄셈 후에 곱하는 것은 뺄셈을 한 다음 곱하는 것과 다릅니다.

이런 경우에 이 ordered()방법을 사용하면 기대 사항이 설정된 순서대로 실행됩니다.

  1. 스파이는 당신의 친구입니다

전체 모의나 부분 모의를 만드는 대신 스파이를 사용할 수 있습니다. 스파이는 객체가 사용된 후 일부 메서드가 호출되었는지 여부를 확인하는 방법입니다.

예상 메서드가 호출되면 반환할 내용을 설정할 기회도 있습니다. 다행히도 Mockery는 andReturnArg()및 를 사용하여 수신한 인수에 따라 반환할 내용을 추가로 수정할 수 있습니다 andReturnUsing().

첫 번째 방법은 인수의 인덱스에 따라 인수를 그대로 반환하는 반면, 두 번째 방법은 최종 결과를 추가로 변경하기 위해 콜백을 허용합니다.