4

I'm just getting used to unit testing and on my new project I decided to adopt a TDD approach.

My code below is to test the UserServices class which is responsible for creating a user, deleting a user, checking if a user exists etc.

Am I on the right lines? I ask because everywhere I look it discusses mocking and I can't help but think I should be mocking predis (a package used for interacting with Redis in PHP). But, mocking it would be difficult as my class expects an instance of Predis.

In the setup I create a dummy user to perform actions on. I may need to create more than one, but at this stage I just use the one. Although, there are methods such as createUser that would need to create an entirely new user.

<?php

namespace VoteMySnap\Tests;

class UserServicesTest extends \PHPUnit_Framework_TestCase
{
    const HOST = '127.0.0.1';
    const PORT = 6379
    const AUTH = NULL;

    public $predis;

    public function setUp()
    {
        $this->predis = new \PredisClient([
            'host' => HOST,
            'port' => PORT
        ]);

        /*
         * Create a dummy user
         */
        $dummyUser = [
            'user:1' => [
                'snapchat_username' => 'james16',
                'password' => 'somesecret',
                'email' => '[email protected]'
            ]
        ];

       /*
        * Insert user into Redis
        */ 
        $this->predis->hmSet(key($dummyUser), $dummyUser[key($dummyUser)]);
    }

    public function tearDown()
    {
        /*
         * Remove the dummy user
         */
        $this->predis->delete(key($dummyUser));
    }  
}

Am I on the right tracks here?

4
  • 1
    "A test with dependencies is an integration test, not a unit test" ... ah, so I'm integration testing. I can't seem to get this right. I only get a little time to study and every time I go to "give it a shot" I end up doing it wrong and then scrap the Unit Testing completely. I'll do some reading on mocking and see what I can dig up. Commented Feb 4, 2016 at 14:08
  • It's better to write your functions so that they require as little mocking as possible. That's one of the purposes of TDD: to show you ways in which you can write your code to be more readily testable. Commented Feb 4, 2016 at 15:53
  • 1
    Unit tests are meant to be isolated from anything but the tested unit. They need not know about the rest of the application, nor should they care about the dependencies. If you feel like the dependency is worth testing, make a test for that. But where does it end? Sometimes testing something really small does not really make sense. What you decide to be a unit is in the end up to you, however, if you require redis or database for the test, that is usually not a unit test anymore, because it depends on a system installed within your environment.
    – Andy
    Commented Feb 4, 2016 at 17:25

3 Answers 3

2

I have always followed the maxim that a unit test tests one thing and only one thing. Programming such that each method does one thing and one thing only, should mean you can write a unit test for that method fairly easily.

If your method calls out to any other services or classes, then you should mock those so their behavior during the test is completely known. PHPUnit allows you to mock classes including those in packages you are using, eg PredisClient. You would want to also mock the hmSet method. I would recommend reading the following to gain a better understanding of how PHPUnit does this.

https://phpunit.de/manual/current/en/test-doubles.html

You may also want to refactor your code to instantiate PredisClient outside of the class you are testing (if you don't already) and then inject it via constructor parameter as this allows your mocked version to be easily injected. Search for Dependency Injection to see how this can help!

1

One of the principles I try to stick to when testing is Don't Mock What You Don't Own.

When you're using a third party library, chances are that you don't need all of it - only a small subset. Also, you might not want the semantics of that library to pervade your own application, as what it does may be expressed in slightly different terms on your side.

Therefore, wrap the external component inside an adapter class of your own. Expose only the capabilities you know your application needs, and give them names that are consistent with the rest of your application. Put an abstraction on top of that wrapper so that it can be easily substituted for other implementations in tests or elsewhere.

Unit test your classes that consume these features by stubbing the wrapper, but integration test the wrapper itself against the real third party module if possible.

0

In all cases of Unit Testing, you have to define what a unit is. Its impractical to make it too small, and unhelpful to make it too big.

Martin Fowler says to make it a class, sometimes (importantly):

"I often take a bunch of closely related classes and treat them as a single unit"

I assume he means that a unit for him is a class, but sometimes, if it contains tightly-coupled dependencies, he will consider those as part of the unit.

You can make unit testing hard for yourself, or you can be pragmatic and keep life easy. The whole point of unit testing is to check if your code works. So if you can include this difficult-to-remove dependency in your tests, can figure out if any errors are due to the dependant object or your code, and the test runs are very fast and do not require extensive setup... then keep it in.

If any of those conditions are not true, then you will have to either figure out how to mock it away, or ignore unit testing and pout all your efforts into integration testing (which you have to do anyway, unit tests only go a short way to making a correct system)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.