Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

mdupor's avatar

assertSee returning green when it shouldn't

I am following this series https://laracasts.com/series/lets-build-a-forum-with-laravel/episodes/3

The issue I'm having is that tests are returning green when they should have failed. The method is the same as Jeff is using (together with a setUp()), and DB for phpunit is in-memory sqlite:

public function a_user_can_read_replies_that_are_associated_with_thread()
{
    $reply = factory(Reply::class)->create(['thread_id' => $this->thread->id]);

    $this->get('/threads/' . $this->thread->id)
        ->assertSee($reply->body);
}

This should fail as the view is using foreach on a non existing relationship @foreach ($thread->replies as $reply).

Clearly enough, when I visit the following URL locally it throws an error, but tests pass.

If I change the test to instead use assertStatus(200) it fails because it throws 500 instead.

I tried dumping the $reply within test itself and it is generated fine.

Is the method itself flawed or am I doing something wrong?

EDIT: Laravel version 6.2, phpunit 8.0

0 likes
31 replies
Nakov's avatar

@mdupor are you sure you are running this test at all? Do you have:

/** @test */    

over the test?

mdupor's avatar

Yes, just didn't paste that part for brevity.

Nakov's avatar

@mdupor hm, I am chaining the status to the check always, so never had such an experience. This is what I do:

$this->get('/threads/' . $this->thread->id)
    ->assertOk()
    ->assertSee($reply->body);  
bugsysha's avatar

This should fail as the view is using foreach on a non existing relationship @foreach ($thread->replies as $reply).

Maybe it is cause of the way your php is setup cause I think it will only throw a warning if you pass null to foreach. So maybe that warning is handled by framework. But why does it passes assertSee, I have no idea.

mdupor's avatar

Even if it does do that, assertSee() checks for content of a displayed page. Body for this reply is a lorem ipsum paragraph so there is no way it can be shown on the page. Even if the relationship is null and page can be rendered correctly, it will never enter the foreach block which actually shows the reply body

Nakov's avatar

@mdupor is the text contained in the error page ?

Will this pass:

$this->get('/threads/' . $this->thread->id)
    ->assertSee('testing');  
mdupor's avatar

No, as I answered in my reply to @bugsysha, can't be rendered when it doesn't enter the foreach block. From its perspective, it doesn't know what $thread->replies is, so it can't know what $reply->body is, thus it can't show it.

Nakov's avatar

@mdupor I am not saying for the rendered view. The error might show a full collection of replies to the thread and the string is within there, so the message can be seen in the error page, not on the original view that you are trying to show. I edited my previous reply. Does that passes too?

mdupor's avatar

Yeah, that one passes as well. Seems it passes whatever I give it

Nakov's avatar

@mdupor are you filtering to run only that test? Do you maybe have the same name of a test and it always passes? Can you break the test somehow? :)

Just saying, because I have Laravel 6.5.2 on my project, and I can break assertSee.

mdupor's avatar

It is literally the same set of tests as Jeff has it in the series. It has 3 tests and all 3 pass. I can comment out every one except that last one. If that is the case then expected I get OK (1 test, 1 assertion). I don't know how can I further debug this, do you have some ideas?

bugsysha's avatar

it doesn't know what $thread->replies

It is null from it's perspective at that point, and I guess by default, php will just throw a warning for that when you pass null to foreach.

so it can't know what $reply->body is, thus it can't show it.

Yes.

Is there anything interesting in the issues for PHPUnit or Laravel repos on GitHub?

bugsysha's avatar

Just tested it. I get Invalid argument supplied for foreach(). Do you have $this->withoutExceptionHandling(); call in your tests?

mdupor's avatar

No, test looks like this:

class ReadThreadsTest extends TestCase
{
    use DatabaseMigrations;

    private $thread;

    protected function setUp(): void
    {
        parent::setUp();
        $this->thread = factory(Thread::class)->create();
    }
    ...
}

I have even tried adding $this->withExceptionHandling() explicitly to a setUp(), but it didn't change a thing.

mdupor's avatar

Also if it helps, these are the factories:

$factory->define(User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'password' => 'yIXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
        'remember_token' => Str::random(10),
    ];
});

$factory->define(Thread::class, function (Faker $faker) {
    return [
        'title' => $faker->sentence,
        'body' => $faker->paragraph,
        'user_id' => factory(User::class)->create()->id,
    ];
});

$factory->define(Reply::class, function (Faker $faker) {
    return [
        'user_id' => factory(User::class)->create()->id,
        'thread_id' => factory(Thread::class)->create()->id,
        'body' => $faker->paragraph,
    ];
});
bugsysha's avatar

Then I would go with what @nakov suggested. Always use assertOK() or assertStatus() calls. It is a nice habit that I also use. Just think of it as when you call once() or times() with mocks. And it makes your app that more tested.

mdupor's avatar

Well yeah, but it kinda doesn't answer why does this happen :)

mdupor's avatar

Hmm...adding $this->withoutExceptionHandling(); actually resolves it.

I thought that this would conceal any exceptions, I was wrong obviously.

1 like
JamesFreeman's avatar

You may find that your error handler is displaying your model which have been implicitly binded?

bugsysha's avatar

Hmm...adding $this->withoutExceptionHandling(); actually resolves it.

Yeah. You have to be careful with the way you write tests and also where you place withoutExceptionHandling() call. Glad it worked.

JamesFreeman's avatar

Can you show your controller for $this->get('/threads/{thread}')

mdupor's avatar

It's a standard REST controller

    public function show(Thread $thread)
    {
        return view('threads.show', compact('thread'));
    }
JamesFreeman's avatar

Yup, you might find that because you are binding Thread to the controller, it'll show the model in html due to ignition error handing.

bugsysha's avatar

@mdupor that is not the solution. It is only for you to see what is failing and to help you debug the issue. That should not remain in your tests.

mdupor's avatar

@bugsysha I understand, but what do you propose if without it I can't see that it actually fails? If what @jamesfreeman says happens, then the only other solution is to chain assertStatus(200) before the assertSee() call. It is hard debugging something if no error is actually thrown.

JamesFreeman's avatar

You should 100% chain $response->assertOk()->assertSee($thread->reply).

bugsysha's avatar
bugsysha
Best Answer
Level 61

If you check my previous response

Then I would go with what @nakov suggested. Always use assertOK() or assertStatus() calls. It is a nice habit that I also use. Just think of it as when you call once() or times() with mocks. And it makes your app that more tested.

tjbancroft's avatar

This is an old thread but none of the great responses were able to solve this issue for me, so I wanted to share my resolution for those who encounter the same problem. I was having the exact same behaviour as @mdupor on Laravel 6.2. I commented out the first two tests to isolate the one that should be failing and I got the warning:

  1. Warning No tests found in class "Tests\Feature\ReadThreadsTest".

I then added 'test_' to the front of the method name and then ran phpunit again. This then failed as it should.

So, the final method name looks like this:

public function test_a_user_can_read_replies_that_are_associated_with_a_thread()

Hopefully my findings help others experiencing the same issue.

kreierson's avatar

If someone else runs across this. The reason this was happening for me was because my view was erroring out because in my test I didn't have everything set up for the page I was trying to view so the view was missing data to display correctly. Which in turn for some reason just passes assertSee no matter the string.

Do a

dd($response->getContent());

To verify your view is rendering properly during your test. Only then can you assert your string exists on the page properly.

At least what I have found.

Tray2's avatar

@kreierson Open a new thread for your question and the likely hood of getting an answer increases.

Please or to participate in this conversation.