John Smith
John Smith

Reputation: 6197

Php, is it good to mock database resultset?

In my model:

class UsersModel
{
    public function getUsers()
    {
        return SELECT id,username,rights FROM database
    }
}

and the result is:

array{
    array{'id' => 1, 'username' => 'a', 'rights' => 4},
    array{'id' => 2, 'username' => 'b', 'rights' => 8},
    array{'id' => 4, 'username' => 'c', 'rights' => 7},
}

this then gets formatted:

class Helper
{
    function formatUsers (array $source)
    {
        foreach ($source as $item)
        {
             $item['usernameF'] = $item['username'].'.'.$item['id'];
        }
        return $source;
    }
}

and the result is:

array{
    array{'id' => 1, 'username' => 'a', 'rights' => 4, 'usernameF' => 'a.1'},
    array{'id' => 2, 'username' => 'b', 'rights' => 8, 'usernameF' => 'b.2'},
    array{'id' => 4, 'username' => 'c', 'rights' => 7, 'usernameF' => 'c.4'}
}

and outputting in the view:

foreach ($users as $item)
{
    echo $item['usernameF'].' = '.$item['rights'].'<br>';
}

so far so good, now lets test it.

testModel:

assertEquals ($result, array{
    array{'id' => 1, 'username' => 'a', 'rights' => 4, 'usernameF' => 'a.1'},
    array{'id' => 2, 'username' => 'b', 'rights' => 8, 'usernameF' => 'b.2'},
    array{'id' => 4, 'username' => 'c', 'rights' => 7, 'usernameF' => 'c.4'}
});

testHelper:

here comes the difficulties. I can mock the getUsers() method to enforce a result.

$mock->setMock ($this->once()->method('getUsers')->willReturn(array{
    array{'id' => 1, 'username' => 'a', 'rights' => 4},
    array{'id' => 2, 'username' => 'b', 'rights' => 8},
    array{'id' => 4, 'username' => 'c', 'rights' => 7}
});

assertEquals ($result, array{
    array{'id' => 1, 'username' => 'a', 'rights' => 4, 'usernameF' => 'a.1'},
    array{'id' => 2, 'username' => 'b', 'rights' => 8, 'usernameF' => 'b.2'},
    array{'id' => 4, 'username' => 'c', 'rights' => 7, 'usernameF' => 'c.4'}
}

BUT what if the DB scheme changes? What if I accidentally mistype a field, lets say "usernmame'. Result will be:

array{
    array{'id' => 1, 'usernmame' => 'a', 'rights' => 4},
    array{'id' => 2, 'usernmame' => 'b', 'rights' => 8},
    array{'id' => 4, 'usernmame' => 'c', 'rights' => 7},
}

of course the test of model fails, so I change it too:

testModel:

assertEquals ($result, array{
    array{'id' => 1, 'usernmame' => 'a', 'rights' => 4, 'usernameF' => 'a.1'},
    array{'id' => 2, 'usernmame' => 'b', 'rights' => 8, 'usernameF' => 'b.2'},
    array{'id' => 4, 'usernmame' => 'c', 'rights' => 7, 'usernameF' => 'c.4'}
});

so all test passes - but in production, the view will fail with "wrong index; username"! So what would be the best approach?

Upvotes: 1

Views: 185

Answers (1)

Maxim Krizhanovsky
Maxim Krizhanovsky

Reputation: 26699

Do not verify more than needed. The formatUsers method accepts array and doesn't care where it comes from, neither what keys it has, other than the username and id. It's perfectly fine to "mock" the data when you are testing this method.

What if the schema is changed? This is a question not about your test, but about your code, and it's perfectly valid question. The goal of good design is to minimize the scope of each change. In your case there are multiple objects that know about the names of the fields. If instead of array you are passing an object with a username property, then you can have only one place in the code where the DB result is mapped to the object, and to decouple the names used in the DB with the names used in the code.

btw you can use array_map instead of the foreach loop

Upvotes: 1

Related Questions