Unit testing serialization
Moderator: General Moderators
- Ambush Commander
- DevNet Master
- Posts: 3698
- Joined: Mon Oct 25, 2004 9:29 pm
- Location: New Jersey, US
Unit testing serialization
Hello folks, I've got a sticky unit testing situation that I'd like to resolve. I have created a class that accepts objects to be cached, which it does by serializing the object and writing it to a file.
I don't mind the file writing operation: it's quite safe and there's virtually no chance of collision. However, I'm having difficulty determining whether or not to mock the object that is going to be cached (the one that will be serialized). If I mock it, I'll end up serializing a whole kaboodle of private testing machinery that may or may not be shifting around. If I don't mock it, I have no way of stopping it from calling other dependencies... i.e. it's not a unit test anymore (for an integration test, this is fine, but I'm trying to TDD).
Perhaps the serialization has to be abstracted out and mocked, but that seems rather extreme just for a unit test. It's a single function, no bells and whistles.
I don't mind the file writing operation: it's quite safe and there's virtually no chance of collision. However, I'm having difficulty determining whether or not to mock the object that is going to be cached (the one that will be serialized). If I mock it, I'll end up serializing a whole kaboodle of private testing machinery that may or may not be shifting around. If I don't mock it, I have no way of stopping it from calling other dependencies... i.e. it's not a unit test anymore (for an integration test, this is fine, but I'm trying to TDD).
Perhaps the serialization has to be abstracted out and mocked, but that seems rather extreme just for a unit test. It's a single function, no bells and whistles.
- Maugrim_The_Reaper
- DevNet Master
- Posts: 2704
- Joined: Tue Nov 02, 2004 5:43 am
- Location: Ireland
I don't think you should mock the object being serialised and cached. In a sense the object is itself test data so it should be left alone. I usually just use a stdClass instance. If you need anything more it's more likely you'd end up testing serialize() rather than the class calling serialize(). Does the class commit itself to a specific object type?
create an intercept so you don't have to actually call serialize to test it. Something like comparing the cache stack (array)
Similar sort of thing when testing a DB layer - you can test what the final statement is without needing to call the respective DB function.
Code: Select all
class TestCache extends UnitTestCase
{
public function __construct()
{
parent::__construct();
}
public function testCacheStack ()
{
$cache = new CacheObject();
$objectToBeCached = new stdClass;
$cache->prepareForCache($objectToBeCached);
$this->assertEqual($cache->getStack(), array($objectToBeCached));
}
// or compare strings
public function testCacheString ()
{
$cache = new CacheObject();
$objectToBeCached = new stdClass;
$cache->prepareForCache($objectToBeCached);
$this->assertEqual($cache->getSerialized(), serialize(array($objectToBeCached)));
}
}Similar sort of thing when testing a DB layer - you can test what the final statement is without needing to call the respective DB function.
- Maugrim_The_Reaper
- DevNet Master
- Posts: 2704
- Joined: Tue Nov 02, 2004 5:43 am
- Location: Ireland
I would definitely call serialize(). It's an internal PHP function not a datasource which is supposed to consistently operate on objects. One day PHP might alter that function and I'd prefer if my Unit Tests picked it up should that happen. It's a significant class specific behaviour - avoiding a direct test would appear to hamstring the test by focusing solely on the caching aspect.
- Ambush Commander
- DevNet Master
- Posts: 3698
- Joined: Mon Oct 25, 2004 9:29 pm
- Location: New Jersey, US
Quick note: PHP 4.
stdClass almost works. I'm not enforcing a specific type, so I can get away with putting something random in there. However, before being serialized, I need the object do some __sleep() style functionality. Since I'm on PHP 4, the serializer/cache class has to do this work on the object. But what this means is that methods are going to be called, and an stdClass will fatally error out.
I imagine there are two ways out of this dilemma:
1. Create a custom mock that implements the necessary interface, and destroys the reference to the test case once its done.
2. Get rid of the __sleep() style functionality (as of right now, I'd only need this sort of thing to ensure that the definition object was properly initialized, which I could make sure always happens with a little discipline)
I'm probably going to feel this out myself, but creating an intercept seems (to me, at least) a little over the top, since no processing is happening to the object: it's just being serialized straight. What would you do?
stdClass almost works. I'm not enforcing a specific type, so I can get away with putting something random in there. However, before being serialized, I need the object do some __sleep() style functionality. Since I'm on PHP 4, the serializer/cache class has to do this work on the object. But what this means is that methods are going to be called, and an stdClass will fatally error out.
I imagine there are two ways out of this dilemma:
1. Create a custom mock that implements the necessary interface, and destroys the reference to the test case once its done.
2. Get rid of the __sleep() style functionality (as of right now, I'd only need this sort of thing to ensure that the definition object was properly initialized, which I could make sure always happens with a little discipline)
I'm probably going to feel this out myself, but creating an intercept seems (to me, at least) a little over the top, since no processing is happening to the object: it's just being serialized straight. What would you do?
- Ambush Commander
- DevNet Master
- Posts: 3698
- Joined: Mon Oct 25, 2004 9:29 pm
- Location: New Jersey, US
no, I mean literally mock the __sleep() method..
Code: Select all
class ObjectToBeMocked //dummy definition for mock creation
{
function __sleep() {}
}
Mock::generate('ObjectToBeMocked', 'MockObject');
class TestBlah extends UnitTestCase
{
function TestBlah ()
{
$this->UnitTestCase();
}
function testMockCache ()
{
$cache =& new Cache();
$mock =& new MockObject($this);
$mock->expectOnce('__sleep');
$cache->cache($mock);
}
}- Ambush Commander
- DevNet Master
- Posts: 3698
- Joined: Mon Oct 25, 2004 9:29 pm
- Location: New Jersey, US
- Ambush Commander
- DevNet Master
- Posts: 3698
- Joined: Mon Oct 25, 2004 9:29 pm
- Location: New Jersey, US
Quite, but you don't have to manually type it all out; all you are looking to do is test that your object is performing as expected - i.e. it is serializing a given parameter and writing it to a file. You're not the one who is actually serailizing, that's down to PHP's serialize function, so you can do:
Code: Select all
$this->assertEqual(file_get_contents($theCacheFile), serialize($theObjectBeingSerialized));