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

danieldasilva's avatar

How to mock a Predis client for testing Redis operations

In a Laravel Lumen 5.5 installation, I have created a class that interacts with a Redis instance via Predis. I have the predis/predis and illuminate/redis packages packages and registered the Redis service provider.

My class runs some redis commands by pipelining them, as to save on the number of operations. My manual tests against a local redis server seem to work fine, as I can verify that the data gets written as expected.

I now want to create unit tests for my class with a mocked Predis client. I have found the following library that seems to provide the functionality that I require:

https://github.com/M6Web/RedisMock

It does support all of the operations that I use. However, I am uncertain as how to force the use of this mocked client by my class, since it simply uses the Redis client provided by the Illuminate service provider. Is that even possible?

If not through the use of the RedisMock library, are there any other ways for me to write unit tests for the redis operations - without having an actual redis server to support it?

Thanks in advance.

0 likes
9 replies
Drfraker's avatar

Have you considered not mocking it? If you feel that you have to mock Redis, why do you feel that way? Redis is so fast I don't think you can make a case for mocking it for speed reasons. The mocking library you are referring to claims to have un-mocked Redis features that could be a hangup. Consider using Redis instead of the mocking library, unless you have a specific reason why you can't.

If you cannot use Redis, can you extract the parts of the class that are calling redis to another class or repository and then mock that?

class SomeClassThatCallsRedis {
    public function doSomething($params){
        // do some stuff

        $this->store($params);
        
        return $value;
    }

    private function store($params) 
    {
        // store some things using redis commands here...
    }
}

becomes

class SomeClassThatCallsRedis {

    public function doSomething($params){
        // do some stuff

        $this->store($params);
        
        return $value;
    }

    private function store($params) 
    {
        $redis = app(RedisRepository::class);
        return $redis->store($params);  
    }
}

class RedisRepository {
    public function store($params) {
        // store some things using redis commands here...
    }
}

In your test

public function test_some_stuff() {
    $subject = new SomeClassThatCallsRedis();
    $redis = Mockery::mock(RedisRepository::class);
    app()->instance(RedisRepository::class, $redis);

    $redis->shouldReceive('store')
            ->with($params)
            ->once()
            ->andReturn(true);

    $subject->doSomething($params);

    
}

Obviously this is pseudo code but you get the general idea. Architect your class so you can mock the Redis store.

1 like
danieldasilva's avatar

Well the issue here is not that I must mock it, but that I need to test the Redis functionality without having an actual Redis server available to serve the test - in other words, I want to be able to run the unit tests anywhere.

MikeHopley's avatar

Well the issue here is not that I must mock it, but that I need to test the Redis functionality without having an actual Redis server available to serve the test - in other words, I want to be able to run the unit tests anywhere.

You might ask yourself what you are actually testing with the mocked tests. Often with these kinds of tests, you are not really testing anything except some trivial "wiring", like "Class A calls some method on class B, which returns a mocked value".

The most important quality of a test is that it is reliable -- i.e. that the test will fail if there is a problem with the code. Sometimes mocking too much can undermine the reliability of tests and give a false sense of confidence.

If I wanted to be confident that my code correctly interacts with a Redis server, then I would test against a Redis server.

Drfraker's avatar

Well the issue here is not that I must mock it, but that I need to test the Redis functionality without having an actual Redis server available to serve the test - in other words, I want to be able to run the unit tests anywhere.

This makes sense to me in the context of mocking an external API call where you need internet or something. However, with redis you can install it on your VM or local machine that your tests are running on. For instance I have redis installed on my Macbook Pro locally and use Valet to serve the application. But that could just as well be a Virtual machine running a vagrant setup. In any case you can install Redis so that your testing environment has access to it. There is no reason I can think of that would prevent you from running your tests if you were not mocking Redis calls.

Let me know if I'm missing something obvious here.

In the case of mocking Redis, did the example I outlined make sense?

nestormata's avatar

I know the question is old, but worth mentioning for anyone looking for this now. Redis in Laravel is registered with a Service Provider.... https://github.com/laravel/framework/blob/5.8/src/Illuminate/Redis/RedisServiceProvider.php

So, with anything that uses a Service Provider is easy to change that registration in the app during testing and inject a mock object there, so that in anyplace that uses Redis it would get your mock object instead. This applies to anything that uses a service provider, and therefore, this is why you are encouraged to write your own service providers in your code instead of instantiating objects in your code, that way you can inject mock objects and have a code that is easier to test and maintain and more loose coupled.

Please or to participate in this conversation.