Summer Sale! All accounts are 50% off this week.

1k3r's avatar
Level 6

Assert view component is rendered

Hello,

We have a blade component PostList and a page that renders the component.

We have a unit test that test the component view variables are OK.

But we want to make a feature test something like this:


$response = $this->get('/posts');

// desirable assert
$response->assertComponentRendered(PostList::class);

// or...
// mock the component and test that render method is called?

$response->assertOk();


Not sure but, could we mock the component and test that "render" method is called?

There is a github idea that needs the same: https://github.com/laravel/ideas/issues/2650

Thanks.

0 likes
11 replies
1k3r's avatar
Level 6

I'm trying to mock but does not work

$this->mock(PostList::class, function ($mock) {
   $mock->shouldReceive('render')->once();
});

$response = $this->get('/posts');

// ...

This is the error:

Mockery\Exception\InvalidCountException: Method render(<Any Arguments>) from Mockery_0_App_View_Components_PostsList should be called
 at least 1 times but called 0 times.

What am I missing?

1k3r's avatar
Level 6

Well, don't know why but this works:

$this->app->bind(PostList::class, function () {
   $mock = $this->partialMock(PostList::class);
   $mock->shouldReceive('render')->once();
   return $mock;
});

But this doest not:

$this->partialMock(PostList::class, function ($mock) {
   $mock->shouldReceive('render')->once();
});

Why is this? The second would be the correct one according to this: https://laravel.com/docs/8.x/mocking#mocking-objects

1k3r's avatar
Level 6

@Tray2

I have a feature test of the component using the method you say, and I know that is render correctly.

Then I have two controllers (or more) that use the component, but I don't need to test the content of the component because I "know" it renders correctly (I already have that test), so in the controller tests I only need to assert that the component is rendered.

Tray2's avatar

@1k3r Still I wouldn't mock it since the component might expect certain variables to be available and therefor should not be mocked. But then again that is just me.

andersb's avatar

@1k3r Did you find a way to assert that a given blade component was rendered (perhaps X number of times or with certain data)?

andersb's avatar

@Tray2 This is a valid point, we could assign an identifier like an id/class to each blade component and assert that such a tag appears in the dom like explained assertElementExists('#main').

This does however require identifiers to be added to all components and it's not possible to test the blade component's data.

Ideally, we were able to assert on the components being rendered like:

$response->assertComponentRendered('example'); 
$response->assertComponentRendered('example', function (View $view) {
    return $view->attributes->name === 'Taylor';
}); 
$response->assertComponentRendered('example', function (View $view) {
    return $view->attributes->name === 'Taylor';
}, 3); // Assert that this is rendered exactly 3 times
Tray2's avatar

@andersb A blade component becomes html after the blade file is parsed and the response will be the parsed html. That makes it very hard to assert if a component has been rendered or not. You can only check the resulting html.

To test something like that you would you would indeed need to moch it, and I dont' think it's a great idea to mock in a feature test.

One way would be to wrap the component with a div and give that div an id, then you can test for how many children are rendered. Let's say you compoenent is an article, and you should render three of them.

get('/records')
    ->assertOk()
    ->assertElementExists('#component-wrapper', function (AssertElement $element) {
        $element->contains('article', 3);
    });
1k3r's avatar
Level 6

@andersb we did this:

// some provider
TestResponse::macro('assertComponentRendered', function ($componentClass) {
   Assert::assertTrue(app()->resolved($componentClass), "{$componentClass} not rendered");
});

// test
$response->assertComponentRendered(SomeComponent::class);
andersb's avatar

@tray2 that is actually exactly the approach I took, except that I add a custom parameter dusk="posts" and check for the existence of that (that way I can use it for both Dusk browser test and for HTTP feature tests) (see docs for Dusk selectors):

get('/records')
    ->assertOk()
    ->assertElementExists('[dusk="posts"]', function (AssertElement $element) {
        $element->contains('td[dusk="author"]', 3);
    });

@1k3r your macro seems really nice. It does however only allow to check if the blade component has been rendered at least once, not how many times or with what data correct?

Wonder if it would be possible to use the event composing:* to do this assertion. The following packages seem to use it for showing info about the rendered views including blade components:

Please or to participate in this conversation.