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

robb's avatar
Level 3

Unit testing session wrapper class and client class

I have a class which does some calculations using data stored in a session.

I have wrapped the session in a class which implements an interface, so the code can be used outside of Laravel using the standard php session.

Storage interface:

interface Store
{
    public function put($key, $value);
    public function get($key);
    public function all();
    public function forget($key);
    public function flush();
}

Concrete implementation for Laravel:

use Illuminate\Session\Store;

LaravelSession implements Store
{

    public function __construct(Store $store)
    {
        $this->store = $store;
    }

    public function put($key, $value)
    {
        $this->store->put($key, $value);
    }
    …
}

A second class PhpSession wraps $_SESSION (for non laravel apps)

Storage injected into ClientClass:

class ClientClass
{
    public function __construct(Store $store)
    {
        $this->storage = $store;
    }

    public function doStuff()
    {
        …
        $this->storage->put($key, $value);
    }
}

Testing

For unit testing the ClientClass I’m able to mock the Store object and return a value or array depending on what I expect to be returned from either concrete session classes.

What i'm not sure about is how to go about unit testing the session classes themselves: LaravelSession and PhpSession.

Also, is there a better name for wrapper class? Is it a Proxy class?

Any help would be greatly appreciated!

0 likes
3 replies
ifpingram's avatar

@robb there are a couple of ways you could test them;

  1. Test directly against concrete classes and not mock anything; the session object and Store are lightweight enough that this will not be any overhead.

  2. Mock the Illuminate\Session\Store interface, then swap out the instance in Laravel's Service Container (IOC), like:

public function testLaravelSessionStore()
{
  $store = Mockery:mock(Illuminate\Session\Store::class, ['get' => 'bar']);

  $this->app->instance(Illuminate\Session\Store::class, $store);

  $laravel_session_store = app(LaravelSession::class);

  $laravel_session_store->put('foo', 'bar');

  $this->assertEquals('bar', $laravel_session_store->get('foo');
}

(note; I have just written this example into the form, not actually tested it - there may be some minor errors, but it gives you an idea of which direction to proceed in).


This then raises a few thoughts:

  1. The mocking of the Illuminate\Session\Store class in the second example hardly seems worthwhile, and in fact could be counter-intuitive in many cases, such as my example, which gives you the feeling of having test coverage, but in fact doesn't. In this case, are we testing the get or the put method? As your LaravelSession->put() method returns nothing, it can only be tested to see if it worked, using a call to the get() method after. As the get method is mocked, then we are still not actually asserting that the put() method succeeded. It's all sleight-of-hand ;)

  2. In light of the above fact, is it worthwhile mocking the Store object to test the LaravelSession class? In my opinion it is not. You can do as per my first suggestion and test against concrete collaborators.

  3. Given the fact you can test against concrete collaborators, is it even worthwhile writing these tests for your wrappers at all? Sure, if you want a high level of code test coverage (which is an anti-pattern IMO also). The reality is that your wrapper classes are doing nothing but as you say marshalling the commands through to the underlying classes they wrap. There is no real business logic there to test and you are in fact testing them (by proxy) when you write your tests for functions like ClientClass->doSomething().

Therefore, in such cases, I would opt to test at a slightly higher level of granularity, and not worry about unit testing these wrapper classes at all.

As for names; I am sure there is some design pattern for these wrapper classes, but I'm not very familiar with names of patterns; I spend my time working in the the code not the theory ;)

Good luck!

robb's avatar
Level 3

@ifpingram thanks so much for writing such a detailed response!

I’m still working through some of it but it’s incredibly helpful, thank you.

ifpingram's avatar

@robb A couple of things have occurred to me since I wrote this post early this morning...

Firstly, the mocking is only really useful in a unit level test, in the above example, to test that the correct methods have been called on the Store collaborator, using [shouldReceive()](http://docs.mockery.io/en/latest/reference/expectations.html_, or even using a Mockery::spys instead.

In fact, just been chatting with a colleague and I realise there is also another way to test this at a lower level, which is probably the correct way also if you wish to go for a unit type test with real collaborators. In essence, you use real collaborators for the Arrange and Act phases, but then use a bit of custom code outside of the application for the assertion. For example (assuming Redis as the cache):

public function testLaravelSessionStore()
{
  $laravel_session_store = app(LaravelSession::class);

  $laravel_session_store->put('foo', 'bar');

  $redis = new PredisClient();

  $this->assertEquals('bar', $redis->get('foo'));
}

I hope this makes sense?

Please or to participate in this conversation.