如何在测试 Laravel 控制器方法时模拟控制器方法内调用的方法

问题描述 投票:0回答:1

web.php
中定义的路线是

Route::get('about-us', [PageController::class, 'renderAboutUsPage'])->name('pages.about-us');

在我的控制器中,我有方法

class PageController extends Controller
{
    protected $pageService;

    /**
     * Class contructor
     */
    public function __construct(PageService $pageService)
    {
        $this->pageService = $pageService;
    }

    /**
     * Method to render about us page
     */
    public function renderAboutUsPage(){
        return $this->renderStaticPage('about-us');
    }

    /**
     * Method to render static page
     * @param slug
     */
    private function renderStaticPage($slug) {
        Log::info("Rendering ".$slug." static page.");
        $page = $this->pageService->getActivePageBySlug($slug);
        return view('pages.static-page', ['data'=>$page]);
    }
}

当我测试方法时,我的理解是这样的

renderAboutUsPage()
那么我应该在测试中模拟
pageService->getActivePageBySlug($slug)
,这样就可以避免真正调用这个方法。这将有助于减少测试执行时间。

我的服务有单独的测试,我正在独立测试

getActivePageBySlug()

我的测试用例是

    /**
     * @test
     * @testdox Whether the about us page returns a successful response, renders the correct view, contains view object {data} and log correct messages in log file.
     * @group website
     * @group static-pages
     */
    public function test_whether_about_us_page_renders_successfully()
    {
        Log::shouldReceive('info')->once()->with("Rendering about-us static page.");
        Log::shouldReceive('info')->once()->with("Getting page by active status and provided slug.");
        $response = $this->get('about-us');
        $response->assertStatus(200);
        $response->assertViewIs('pages.static-page'); 
        $response->assertViewHas('data');
    }

我不知道如何在我的测试用例中模拟方法

getActivePageBySlug($slug)
以避免真正的调用。

getActivePageBySlug($slug)
方法的定义是:

    /**
     * Method to get page by slug (Checks to page status is active)
     * @param string slug
     * @return Page page
     * @throws Throwable e 
     */
    public function getActivePageBySlug(string $slug)
    {
        Log::info("Getting page by active status and provided slug.");
        try
        {
            $page = Page::where('slug',$slug)->where('status', Status::active->value())->first();
            if(!$page) {
                throw new NotFoundHttpException("The page ". $slug ." not found.");
            } 
            return $page;
        }
        catch (Throwable $e)
        {
            Log::error("Error in getting page by slug.");
            throw $e;
        }
    }
laravel automated-tests phpunit
1个回答
0
投票

实际上,您不想避免调用,否则它并不是真正的功能测试,而是“单元测试”。如果您正在项目中测试端点/url,那么它应该是功能测试,您应该进行真正的调用,除非它是您真正想避免调用的第三方服务。

您的服务正在执行数据库调用,您应该调用它,这很好,特别是您在调用之后没有做任何繁重的工作。

所以,如果你想进行真正的调用,你应该有这样的代码(假设你有工厂并且一切都正确设置):

/**
 * @group website
 * @group static-pages
 */
public function test_about_us_page_should_render(): void
{
    Page::factory()->create([
        'slug' => $page = 'about-us',
        'status' => Status::active, // Be sure to cast 'status' => Status::class in your model
    ]);

    Log::shouldReceive('info')->once()->with("Rendering {$page} static page.");
    Log::shouldReceive('info')->once()->with("Getting page by active status and provided slug.");

    $this->get($page)
        ->assertOk()
        ->assertViewIs('pages.static-page')
        ->assertViewHas('data');
}

这应该是有史以来最简单的测试,您可以在那里添加更多东西。看到我更改了测试名称(试图遵循标准),以及其他小的更改。

现在,假设您还希望页面失败(您的服务抛出错误),所以我们也测试一下:

/**
 * @group website
 * @group static-pages
 * @depends test_about_us_page_should_render
 * @dataProvider pageErrorDataProvider
 */
public function test_about_us_page_should_throw_an_error_when_model_not_found(callable $pageSeeder): void
{
    $pageSeeder();

    $page = 'about-us';

    Log::shouldReceive('error')->once()->with("Error in getting page by slug.");

    $this->get($page)
        ->assertServerError(); // This checks for status = 500

    // You should also assert that you got the exact error: "The page {$page} not found."
}

public function pageErrorDataProvider(): array
{
    return [
        'Page not active' => function () {
            return Page::factory()->create([
                'slug' => 'about-us',
                'status' => Status::disabled, // Or whatever the other state is
            ]);
        },
        'Different page' => function () {
            return Page::factory()->create([
                'slug' => 'about',
                'status' => Status::active,
            ]);
        },
        'Page does not exist' => function () {
            return null;
        },
    ];
}

现在,你可以看到我们首先依赖第一个测试,如果 happy path 有效,这个新的 case 也可以运行。我们还利用了数据提供者,因此我们使用相同的情况测试不同的可能性,只是不同的数据初始化。

我现在不记得从端点检查错误的正确断言是什么,所以我们还确保检查错误是否是预期的错误,但是请阅读官方文档并修改它并你会找到它的。

最后,如果您确实想避免进行任何通话,那么您的测试应该是:

/**
 * @group website
 * @group static-pages
 */
public function test_about_us_page_should_render(): void
{
    $page = 'about-us';

    $this->mock(PageService::class, function (MockInterface $mock) {
        $mock->shouldReceive('getActivePageBySlug')
            ->andReturn(
                Page::factory()->make([
                    'slug' => $page = 'about-us',
                    'status' => Status::active,
                ])
            );
    });

    Log::shouldReceive('info')->once()->with("Rendering {$page} static page.");
    Log::shouldReceive('info')->once()->with("Getting page by active status and provided slug.");

    $this->get($page)
        ->assertOk()
        ->assertViewIs('pages.static-page')
        ->assertViewHas('data');
}

你想模拟服务,这样你就可以做任何事情。

请记住,我使用了 Laravel 11.x 文档,并且我不知道你的

Page
有什么内容,你还应该断言返回的内容是预期的内容,而不仅仅是它有
data
索引您的观点,以及它是什么数据。

© www.soinside.com 2019 - 2024. All rights reserved.