In this lesson, I will show you the difference between asserting something with see and asserting data. Because assertSee
may be dangerous and incorrect.
First, let's see why assertSee
could be dangerous. For example, if we create some product with the name Product 1
and search for the product's name on the page.
use App\Models\Product;use Illuminate\Foundation\Testing\RefreshDatabase; class ProductsTest extends TestCase{ // ... public function test_homepage_contains_non_empty_table(): void { Product::create([ 'name' => 'Product 1', 'price' => 123, ]); $response = $this->get('/products'); $response->assertStatus(200); $response->assertDontSee(__('No products found')); $response->assertSee('Product 1'); }}
But what if the text Product 1
is present somewhere else on that page? So, for this example, let's remove the name from showing in the table and add some text above the table.
resources/views/products/index.blade.php:
// ... <h2>You will see Product 1 and other great products</h2> <table class="min-w-full divide-y divide-gray-200 border"> <thead> <tr> <th class="px-6 py-3 bg-gray-50 text-left"> <span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Name</span> </th> <th class="px-6 py-3 bg-gray-50 text-left"> <span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Price</span> </th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200 divide-solid"> @forelse($products as $product) <tr class="bg-white"> <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900"> {{ $product->name }} </td> <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900"> ${{ number_format($product->price, 2) }} </td> </tr> @empty <tr class="bg-white"> <td colspan="2" class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900"> {{ __('No products found') }} </td> </tr> @endforelse </tbody> </table>// ...
If we visit the /products
page, we will see that the table name is empty, but the Product 1
is still present on the page.
Now, let's run the tests.
As you can see, the tests are all green, but there is no text in the table. So what is the solution?
A better way is to test the data. Sometimes, you can still use assertSee
and assertDontSee
, but a better way is to use assertViewHas
.
In this example, we assert that the key passed in our view is products
and that there is a value. If it's just a value, the second parameter can be just it, but in this case, it's more complicated, so we must use a closure. This test will be even better because we will test the value's name and price here.
To test that, first, when a product is created, we must assign it to a variable. And then, in the callback, check if the collection that is passed from the Controller contains that product.
tests/Feature/ProductsTest.php:
use App\Models\Product;use Illuminate\Database\Eloquent\Collection; class ProductsTest extends TestCase{ // ... public function test_homepage_contains_non_empty_table(): void { $product = Product::create([ 'name' => 'Product 1', 'price' => 123, ]); $response = $this->get('/products'); $response->assertStatus(200); $response->assertDontSee(__('No products found')); $response->assertSee('Product 1'); $response->assertViewHas('products', function (Collection $collection) use ($product) { return $collection->contains($product); }); }}
Undo the View file.
resources/views/products/index.blade.php:
// ... <h2>You will see Product 1 and other great products</h2> <table class="min-w-full divide-y divide-gray-200 border"> <thead> <tr> <th class="px-6 py-3 bg-gray-50 text-left"> <span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Name</span> </th> <th class="px-6 py-3 bg-gray-50 text-left"> <span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Price</span> </th> </tr> </thead> <tbody class="bg-white divide-y divide-gray-200 divide-solid"> @forelse($products as $product) <tr class="bg-white"> <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900"> {{ $product->name }} </td> <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900"> ${{ number_format($product->price, 2) }} </td> </tr> @empty <tr class="bg-white"> <td colspan="2" class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900"> {{ __('No products found') }} </td> </tr> @endforelse </tbody> </table>// ...
Now, let's re-launch the tests again.
And it is green. Now, we are testing for both things. The text is seen on the page, and the data is passed correctly.