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

temo's avatar
Level 2

How to test filter query

Hello,

How can I test unit filter query?

0 likes
23 replies
Sinnbeck's avatar

Make a test to the route that uses it and assert it has the excepted data returned to the view

temo's avatar
Level 2

@Sinnbeck Yes that's only possible way but I want to get more information about how to do that, if it's possible

Sinnbeck's avatar

@temo not the only way, but it is one way :)

Which part are you struggling with?

1 like
Sinnbeck's avatar

@temo be aware that unit tests in don't use database. So I would still use feature tests. But as it is tied deeply to a request you are also forced by that

Edit: I just saw that @kevinbui fixed the dependency of request for you :)

kevinbui's avatar
kevinbui
Best Answer
Level 41

To make this a little bit simpler to unit test, I add another parameter to the query scope:

public function scopeFilter($query, $search = null)
{
	if ($search) {
		$query->whereRaw('UPPER(name) LIKE ?', ['%' . strtoupper($search) . '%']);
	}
    
    return $query;
}

Just to elaborate on @sinnbeck answer, you can write a simple test case like this for the scope.

public function test_filter_scope()
{
    $first = CountryStatistics::factory()->create(['name' => 'England']);
    $second = CountryStatistics::factory()->create(['name' => 'United States']);

    $results = CountryStatistics::filter('United')->get();

    $this->assertFalse($results->contains($first));
    $this->assertTrue($results->contains($second));
}
1 like
Sinnbeck's avatar

@kevinbui you accidentally used the request.

$query->whereRaw('UPPER(name) LIKE ?', ['%' . strtoupper($search)) . '%']); 
1 like
temo's avatar
Level 2

@kevinbui

I tried this:

	public function test_filter_scope()
	{
		$first = CountryStatistics::factory()->create(['name' => 'England']);
		$second = CountryStatistics::factory()->create(['name' => 'United States']);

		$results = CountryStatistics::filter('United')->get();
		
		$this->assertFalse($results->contains($first));
		$this->assertTrue($results->contains($second));
	}

I am getting error, when using dd($result->contains($first)) or contains($second) I'm getting true on both of them.

So error is : • Tests\Feature\ProjectsTest > filter scope Failed asserting that true is false.

temo's avatar
Level 2

Fixed the error, test works fine and coverage became 100% but now I cant filter on localhost. Would it be okay if I use 2 functions inside this model? One for test and one for actual search:

public function scopeFilter($query, $search = null)
	{
		if (request('search'))
		{
			$query->whereRaw('UPPER(name) LIKE ?', ['%' . strtoupper(request('search')) . '%']);
		}

		if ($search)
		{
			$query->whereRaw('UPPER(name) LIKE ?', ['%' . strtoupper($search) . '%']);
		}

		return $query;
	}
Sinnbeck's avatar

@temo then you completely remove the idea of the test. Fix your code instead. Show how you are using it in the actual code and I can help

And what if you ever need the scope in a command or job? Those don't have any request so it will never work

It should be something like

->filter(request('search'))->get() 
temo's avatar
Level 2

Sorry for stupid question, by using two functions I can't get coverage of 100%. So problem is that with

		if (request('search'))
		{
			$query->whereRaw('UPPER(name) LIKE ?', ['%' . strtoupper(request('search')) . '%']);
		}

Filter works in project, and with :

if ($search)
		{
			$query->whereRaw('UPPER(name) LIKE ?', ['%' . strtoupper($search) . '%']);
		}

		return $query;

Test is successful. How can I use the second one so that it works in project too?

temo's avatar
Level 2

@Sinnbeck

So here's my Model filter:

public function scopeFilter($query, $search = null)
	{
        
		if ($search)
		{
			$query->whereRaw('UPPER(name) LIKE ?', ['%' . strtoupper($search) . '%']);
		}
		return $query;
	}

As I mentioned above it gives 100% coverage but doesnt work in project.

There is search form:

  <div class="mt-2 md:mt-10">
            <section class="flex md:border h-[45px] md:w-[270px] p-1 rounded-lg md:border-2">
                <form method="GET" action="#" class="flex">
                    <button type="submit">
                        <img
                            src="{{url('/images/search.png')}}"
                            alt="not found"
                            class="md:ml-2"/>
                    </button>
                    <input class="w-[200px] ml-3 placeholder:font-medium focus:outline-none"
                           type="text"
                           name="search"
                           placeholder="{{__('texts.search_by_country')}}"
                           value="{{ request('search') }}">
                </form>
            </section>
            <x-table/>
        </div>
Sinnbeck's avatar

@temo coverage cannot be 100 when you are not actually testing your code. See my last answer :)

1 like
temo's avatar
Level 2

@Sinnbeck

public $sortField;

	public $sortAsc = true;

	public function sortBy($field)
	{
		if ($this->sortField === $field)
		{
			$this->sortAsc = !$this->sortAsc;
		}
		else
		{
			$this->sortAsc = true;
		}
		$this->sortField = $field;
	}

	public function render()
	{
		return view('livewire.countries-table', [
			'country_statistics' => CountryStatistics::filter()
				->orderBy(isset($this->sortField) ? $this->sortField : 'created_at', $this->sortAsc ? 'asc' : 'desc')
				->get(),
		]);
	}
temo's avatar
Level 2

@Sinnbeck

'country_statistics' => CountryStatistics::filter(request('search'))
				->orderBy(isset($this->sortField) ? $this->sortField : 'created_at', $this->sortAsc ? 'asc' : 'desc')
				->get(),

Like this?

Sinnbeck's avatar

@temo exactly. Now you are passing the search into the method

1 like
temo's avatar
Level 2

@Sinnbeck

Maybe you can help me with this too? :D

public function store(LoginRequest $request)
	{
		$validated = $request->validated();

		if (auth()->attempt($validated))
		{
			session()->regenerate();     -- this

			$user = Auth::getProvider()->retrieveByCredentials($validated);

			Auth::login($user, $request->get('remember'));

			return redirect(route('worldwide.statistics'))->with('success', 'Welcome back!');  --this
		}

How to test this two iines ? I got 55% coverage but need to hit 100% ;D

Sinnbeck's avatar

@temo please make a seperate thread and I will do my best to help

Please or to participate in this conversation.