Hossein Baghayi
Hossein Baghayi

Reputation: 135

Testing a method which completely uses another objects methods

I am refactoring a class while writing unit tests for it. There is a case that one of my methods is completely calling another object's methods which is injected to this class that I am testing.

So I have to mock the object that I have injected to class.

Now, the questing is that, does it worth to write unit tests for this particular method? It seems to be strange to write unit tests which all it does it calling other object's methods which that object itself has to be mocked, then why am I testing this method at all?

Isn't the purpose of testing to check functionality of a method whether it works as expected or not? If it is, then when I am mocking all it has and there is nothing left of that particular method to test then why should I test?

I am really confused!

The method which I'm stuck at is this (which is used for custom session handling):

public function write($sessionId, $sessionData)
{
    $sth = $this->databaseConnection->prepare("INSERT INTO `{$this->DBTableName}` (`session_id`,`session_name`,`session_data`) VALUES(:session_id, :session_name, :session_data) ON DUPLICATE KEY UPDATE `session_data`=:session_data;");
    $sth->bindValue(':session_id', $sessionId);
    $sth->bindValue(':session_name', $this->sessionName);
    $sth->bindValue(':session_data', $sessionData);

    return $sth->execute();
}

here is link for this piece of code as well: http://pastebin.com/1FBeU6mb

By the way, I am newly started writing tests for my classes and I am beginner in this field of testing and inexperienced.

Thanks in advance.

Upvotes: 3

Views: 175

Answers (3)

localheinz
localheinz

Reputation: 9582

Assuming that your example describes a SessionHandler class, which looks similar to this:

class SessionHandler
{
    public function __construct($sessionTableName, $sessionName, \PDO $databaseConnection)
    { 
        $this->DBTableName = $sessionTableName;
        $this->sessionName = $sessionName;
        $this->databaseConnection = $databaseConnection;
    }

    // among others, your method write($sessionId, $sessionData) follows
}

this could cover the method write():

public function testWriteInsertsOrUpdatesSessionData()
{
    /**
     * initialize a few explaining variables which we can refer to 
     * later when arranging test doubles and eventually act
     */
    $sessionTableName = 'sessions';
    $sessionName = 'foobarbaz';

    $sessionId = 'foo';
    $sessionData = serialize([
        'bar' => 'baz',
    ]);

    $executed = true;

    /**
     * create a test double for the statement that we expect to be returned 
     * from `PDO::prepare()`
     */
    $statement = $this->createMock(\PDOStatement::class);

    /**
     * set up expectations towards which methods should be invoked 
     * on the statement, specifying their order
     */
    $statement
        ->expects($this->at(0))
        ->method('bindValue')
        ->with(
            $this->identicalTo(':session_id'),
            $this->identicalTo(sessionId)
        );

    $statement
        ->expects($this->at(1))
        ->method('bindValue')
        ->with(
            $this->identicalTo(':session_name'),
            $this->identicalTo($sessionName)
        );

    $statement
        ->expects($this->at(2))
        ->method('bindValue')
        ->with(
            $this->identicalTo(':session_data'),
            $this->identicalTo(sessionData)
        );

    $statement
        ->expects($this->at(3))
        ->method('execute')
        ->willReturn($executed);

    /**
     * create a test double for the database connection we inject
     * into SessionHandler during construction
     */
    $databaseConnection = $this->createMock(\PDO::class);

    $databaseConnection
        ->expects($this->once())
        ->method('prepare')
        ->with($this->identicalTo(sprintf(
            'INSERT INTO `%s` (`session_id`,`session_name`,`session_data`) VALUES(:session_id, :session_name, :session_data) ON DUPLICATE KEY UPDATE `session_data`=:session_data;',
            $sessionTableName
        )))
        ->willReturn($statement);

    $sessionHandler = new SessionHandler(
        $sessionTableName,
        $sessionName,
        $databaseConnection
    );

    $result = $sessionHandler->write(
        $sessionId,
        $sessionData
    );

    $this->assertSame($executed, $result);
}

For reference, see:

Upvotes: 0

Doug R
Doug R

Reputation: 5779

I'm not really familiar with php, but it looks like you're just building and executing a database query, right?

Unless there's some other layer between this method and the database later that I don't see, there's really nothing here worth mock away. So you're right in the sense that mocking here does not give you a lot of value. And in one sense, testing this method is of limited value since you are really just testing the database layer, which generally we can assume is already correct and stable, and therefore does not need tests.

The value of mocking in general is that it allows you to simplify your tests by making assumptions about what other methods are doing, and allows you to not have to test those other methods indirectly.

In choosing to test the write method, what it turns out you are testing is that you have the correct steps in place to return the right results. That's it. I'm not familiar with php mocking frameworks, but I do know for frameworks in other languages you can set up what are called expectations, which lets you specify that a certain method will be called on a certain object with certain parameters. You can even often specify the order in which they should be executed. The takeaway here is that you are testing the outgoing messages your object is sending, and not the return values of those messages.

It is up to you to decide whether that is valuable vs. the maintenance this test will require.

Upvotes: 2

Bram Gerritsen
Bram Gerritsen

Reputation: 7238

You are testing if the right arguments are passed to your prepared statement. Besides you should test if the write method returns the prepared statment result.

If you do not test this there are several ways your application could break.

  • Renaming or removing of the methods arguments ($sessionId, $sessionData)
  • Renaming of your property $this->sessionName
  • Removing one of the bindValue calls.
  • Wrong naming of your bind aliases. They should match your query.
  • Returning something else than the result of execute().

etc. etc.

So yes it is good pratice to test this.

Upvotes: 0

Related Questions