thomasdiluccio's avatar

Pest: Test Geocoder store data in Redis

Hi

I'm writing a simple helper that retrieves the country code from an IP address, using geocoder-php/GeocoderLaravel.

I want to test it with PHP. How can I test that values are indeed stored in Redis and not fetched every single time?

public function getCountryCodeFromIp(?string $ip): ?string
    {
        if (!$ip) {
            return null;
        }

        return app('geocoder')
            // ->doNotCache()
            ->geocode($ip)
            ?->get()
            ?->first()
            ?->getCountry()
            ?->getCode()
        ;
    }
test('get country code from IP', function () {
    $pseudomizer = new Geocoder();
    expect($pseudomizer->getCountryCodeFromIp(null))->toBeNull();
;
    expect($pseudomizer->getCountryCodeFromIp('8.8.8.8'))
        ->toEqual('US')
    ;
});
0 likes
1 reply
LaryAI's avatar
Level 58

To test that values are stored in Redis and not fetched every single time, you can use a combination of mocking and assertions on the Redis store. Here's how you can modify your test to ensure that the geocoding result is being cached:

test('get country code from IP and store in Redis', function () {
    $ip = '8.8.8.8';
    $countryCode = 'US';

    // Mock the Geocoder service
    $geocoderMock = Mockery::mock('overload:Geocoder\Laravel\ProviderAndDumperAggregator');
    $geocoderMock->shouldReceive('geocode')->once()->with($ip)->andReturnSelf();
    $geocoderMock->shouldReceive('get')->andReturnSelf();
    $geocoderMock->shouldReceive('first')->andReturnSelf();
    $geocoderMock->shouldReceive('getCountry')->andReturnSelf();
    $geocoderMock->shouldReceive('getCode')->andReturn($countryCode);

    // Replace the Geocoder service with the mock
    app()->instance('geocoder', $geocoderMock);

    // Call the method twice with the same IP
    $pseudomizer = new Geocoder();
    $firstCall = $pseudomizer->getCountryCodeFromIp($ip);
    $secondCall = $pseudomizer->getCountryCodeFromIp($ip);

    // Assert that the country code is correct
    expect($firstCall)->toEqual($countryCode);
    expect($secondCall)->toEqual($countryCode);

    // Assert that the value is stored in Redis
    $redisValue = Redis::get('geocode:'.$ip);
    expect($redisValue)->toEqual($countryCode);
});

In this test, we're using Mockery to create a mock of the Geocoder service. We're telling the mock to expect the geocode method to be called only once with the specified IP address, which means that the second call should retrieve the value from the cache (Redis) instead of hitting the Geocoder service again.

We then replace the Geocoder service in the Laravel container with our mock, so when our getCountryCodeFromIp method calls app('geocoder'), it will use the mock instead of the real service.

After calling the getCountryCodeFromIp method twice with the same IP, we assert that the country code is correct for both calls.

Finally, we check Redis to see if the value has been stored correctly. We're using the key 'geocode:'.$ip to retrieve the value from Redis and assert that it matches the expected country code.

Please note that you need to have the Redis facade available and properly configured for this test to work. Also, make sure to include the necessary use statements for Mockery, Redis, and any other classes or facades you're using in your test file.

Please or to participate in this conversation.