TDD workshop. Anybody welcome; Data storage abstraction.

Discussion of testing theory and practice, including methodologies (such as TDD, BDD, DDD, Agile, XP) and software - anything to do with testing goes here. (Formerly "The Testing Side of Development")

Moderator: General Moderators

User avatar
arjan.top
Forum Contributor
Posts: 305
Joined: Sun Oct 14, 2007 4:36 am
Location: Hoče, Slovenia

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by arjan.top »

I don't think it's correct because the step is just too big ...

Code: Select all

 
<?php
 
require_once dirname(__FILE__) . '/../configure.php';
require_once CLASS_BASE . '/DataStore.php';
require_once CLASS_BASE . '/StorageModel.php';
 
Mock::generate('StorageModel', 'MockStorageModel');
 
class DataStoreTest extends UnitTestCase {
 
  private $_dataStore;
  private $_storageModel;
 
  public function setUp() {
    $this->_storageModel = new MockStorageModel();
    $this->_storageModel->setReturnValue('get', 'bar');
    $this->_dataStore = new DataStore($this->_storageModel);
  }
 
  public function testSetAndGetSingleValue() {
    $this->_dataStore->set('foo', 'bar');
    $this->assertEqual($this->_dataStore->get('foo'), 'bar');
  }
 
}
 

Code: Select all

 
<?php
 
require_once dirname(__FILE__) . '/StorageModel.php';
 
class DataStore {
    
    private $storageModel;
 
    public function __construct(StorageModel $storageModel) {
        $this->storageModel = $storageModel;
    }
  
    public function set($key, $value) {
        $this->storageModel->set($key, $value);
    }
  
    public function get($key) {
        return $this->storageModel->get($key);
    }
 
}
 

Code: Select all

 
<?php
 
interface StorageModel {
    
    public function set($key, $value);
    
    public function get($key);
    
}
 
 
User avatar
arjan.top
Forum Contributor
Posts: 305
Joined: Sun Oct 14, 2007 4:36 am
Location: Hoče, Slovenia

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by arjan.top »

and test for my interface version (I still don't understand the set/get in StorageModel ...)

Code: Select all

 
<?php
 
require_once dirname(__FILE__) . '/../configure.php';
require_once CLASS_BASE . '/DataStore.php';
require_once CLASS_BASE . '/StorageModel.php';
 
Mock::generate('StorageModel', 'MockStorageModel');
 
class DataStoreTest extends UnitTestCase {
 
  private $_dataStore;
  private $_storageModel;
 
  public function setUp() {
    $this->_storageModel = new MockStorageModel();
    $this->_dataStore = new DataStore($this->_storageModel);
  }
 
  public function testGetSingleValue() {
    $this->assertEqual($this->_dataStore->get('foo'), 'bar');
  }
 
}
 

Code: Select all

 
<?php
 
require_once dirname(__FILE__) . '/StorageModel.php';
 
class DataStore {
    
    private $values = array('foo' => 'bar');
 
    public function __construct(StorageModel $storageModel) {
    }
  
    public function set($key, $value) {
    }
  
    public function get($key) {
        return $this->values[$key];
    }
 
}
 
 
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by Chris Corbyn »

Why did you set a return value for StorageModel? We agreed that set/get would only be in DataStore ;)

I already posted the test you have to pass but you've ignored it and written your own. Could you go back and look at the test I provided please? If we all start running off in different directions this thread will become a confused mess ;)

DataStore has get()/set() methods which are self-contained. StorageModel hasn't come into play yet since we haven't tried to save() anything, nor have we mentioned that we need to load() any existing data. Just focus on DataStore.

Hard-coding a value into DataStore, although it makes the test pass, you still should use a little amount of sense and follow the test I posted ;) set() the value first, then get() it. We could write a second test to prove that your class doesn't work if we want to however; I just like to avoid adding that clutter since the first test I posted already expresses what we wanted.

If you'd like me to post what I was expecting I'll do that :)
User avatar
arjan.top
Forum Contributor
Posts: 305
Joined: Sun Oct 14, 2007 4:36 am
Location: Hoče, Slovenia

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by arjan.top »

yes I thought that set/get is in the DataModel like the first interface, my mistake :)

Corrected:

Code: Select all

 
 
<?php
 
require_once dirname(__FILE__) . '/../configure.php';
require_once CLASS_BASE . '/DataStore.php';
require_once CLASS_BASE . '/StorageModel.php';
 
Mock::generate('StorageModel', 'MockStorageModel');
 
class DataStoreTest extends UnitTestCase {
 
  private $_dataStore;
  private $_storageModel;
 
  public function setUp() {
    $this->_storageModel = new MockStorageModel();
    $this->_dataStore = new DataStore($this->_storageModel);
  }
 
  public function testSetAndGetSingleValue() {
    $this->_dataStore->set('foo', 'bar');
    $this->assertEqual($this->_dataStore->get('foo'), 'bar');
  }
 
}
 

Code: Select all

 
 
<?php
 
require_once dirname(__FILE__) . '/StorageModel.php';
 
class DataStore {
   
    private $values;
 
    public function __construct(StorageModel $storageModel) {
    }
 
    public function set($key, $value) {
        $this->values[$key] = $value;
    }
 
    public function get($key) {
        return $this->values[$key];
    }
 
}
 
 
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by matthijs »

I did this to make the test pass

Code: Select all

 
<?php
class DataStore {
 
  private $store = array();
  public function __construct(StorageModel $storageModel) {
  }
 
  public function set($key, $value) {
    $this->store[$key] = $value;
  }
 
  public function get($key) {
    return $this->store[$key];
  }
 
}
?>
 
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by Chris Corbyn »

~matthijs, looks correct. ~arjan.top, almost correct, but you should have been getting notices about the unitialized array. Turn your error reporting to E_ALL ;)

Does anyone have any pressing questions at this stage? :)
User avatar
arjan.top
Forum Contributor
Posts: 305
Joined: Sun Oct 14, 2007 4:36 am
Location: Hoče, Slovenia

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by arjan.top »

I have error reporting set to E_ALL, but I did not test the code, just written it on the forum (yes I broke many TDD rules hehe)
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by matthijs »

Chris Corbyn wrote:Does anyone have any pressing questions at this stage? :)
Yes in fact. I'd like to know What... is the Airspeed Velocity of an Unladen Swallow?

Just kidding :)

Should we go on with a save() method?
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by matthijs »

Thinking about it, if you'd want to test save() like

Code: Select all

public function testSaveSavesSuccesfully(){}
you immediately have the problem that you have to do tons of stuff to make that possible and be able to test it. Should we start with something like

Code: Select all

public function testSaveValueReturnsTrueOnSuccess(){}
public function testSaveValueReturnsFalseOnFail(){}
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by Chris Corbyn »

matthijs wrote:Thinking about it, if you'd want to test save() like

Code: Select all

public function testSaveSavesSuccesfully(){}
you immediately have the problem that you have to do tons of stuff to make that possible and be able to test it. Should we start with something like

Code: Select all

public function testSaveValueReturnsTrueOnSuccess(){}
public function testSaveValueReturnsFalseOnFail(){}
How about starting by making sure save() delegates to the StorageModel? ;) It's probably StorageModel's responsibility to return true/false. We can then make sure that return value is passed back out through DataStore:

StorageModel.php

Code: Select all

<?php
 
interface StorageModel {
  public function save($data);
  public function load();
}
DataStore.php

Code: Select all

<?php
 
require_once dirname(__FILE__) . '/StorageModel.php';
 
class DataStore {
 
  private $_store = array();
 
  public function __construct(StorageModel $storageModel) {
  }
  
  public function set($key, $value) {
    $this->_store[$key] = $value;
  }
  
  public function get($key) {
    return $this->_store[$key];
  }
  
  public function save() {
  }
 
}
DataStoreTest.php

Code: Select all

// ... snip ...
 
  public function testSaveDelegatesToStorageModel() {
    $this->_storageModel->expectOnce('save');
    $this->_dataStore->save();
  }
 
// ... snip ...
 
Failure:

Code: Select all

AllTests.php
1) Expected call count for [save] was [1] got [0] at [/Users/chris/data_store/tests/unit/DataStoreTest.php line 25]
    in testSaveDelegatesToStorageModel
    in DataStoreTest
    in /Users/chris/data_store/tests/unit/DataStoreTest.php
    in All DataStore tests
FAILURES!!!
Test cases run: 1/1, Passes: 1, Failures: 1, Exceptions: 0
Files attached.


Files attached:

PS: I'm ok with either way, but are we prefixing underscores to private properties or not? :) We've got a bit of inconsistency with the test case and the actual code right now. I'll switch my convention to whatever you guys say.

EDIT | By the way, I'm away after today but will be back Friday. It's a long weekend in Australia :)
Attachments
data_store.zip
(136 KiB) Downloaded 187 times
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by matthijs »

How about starting by making sure save() delegates to the StorageModel? ;) It's probably StorageModel's responsibility to return true/false. We can then make sure that return value is passed back out through DataStore:
Ok, had not thought about that. Have also not seen the method expectOnce before. Is that in the documentation? I only see assert..() tests.

At least I was thinking in the right direction about the StorageModel not caring about whether the data get saved for real or not, only about getting a True or False back.

Prefixes: personally I'm not used to them. The funny thing was, it didn't matter for the tests whether I had the underscores or not.
At first sight, I have a small preference not to use them. But maybe that's because I'm not using them normally. It does make it more explicit what is private or not. But then again, does that really matter?

Have a good day off! And thanks for all the help so far, this is a great exercise.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by Chris Corbyn »

matthijs wrote:Have also not seen the method expectOnce before. Is that in the documentation? I only see assert..() tests.
All the mock object methods are "expect" whereas the UnitTestCase methods are "assert" (mostly). You're telling the mock object what you "expect" to happen (because it hasn't happened yet). In something like assertTrue() you're making an assertion about what *did* happen.
At least I was thinking in the right direction about the StorageModel not caring about whether the data get saved for real or not, only about getting a True or False back.
Yeah, you were right :) The introduction of the Mock object playing a role in the test now allows us to focus our test on the behaviour of the DataStore without caring how that save is handled.
Prefixes: personally I'm not used to them. The funny thing was, it didn't matter for the tests whether I had the underscores or not.
At first sight, I have a small preference not to use them. But maybe that's because I'm not using them normally. It does make it more explicit what is private or not. But then again, does that really matter?
It's part preference and part convention really. As long we don't end up with a mix of both in our end product I don't really care :P
Have a good day off! And thanks for all the help so far, this is a great exercise.
Cheers :D The pace we're going at will pick up naturally once we're all on the same wavelength.
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by matthijs »

All the mock object methods are "expect" whereas the UnitTestCase methods are "assert" (mostly). You're telling the mock object what you "expect" to happen (because it hasn't happened yet). In something like assertTrue() you're making an assertion about what *did* happen.
Ok, good to know. Will reread the docs and see if I can find out more.
Cheers :D The pace we're going at will pick up naturally once we're all on the same wavelength.
That's cool. I don't mind taking small steps. At least we think through each one carefully. Now it's a simple get and set, but I imagine if you place the same thoughtfulness in all your methods, in the end you end up with much better code. Going back having to change stuff does take a lot of time as well I guess.
And more importantly, it's more about the testing process right now then it is about the code, isn't it? Normally, because my PHP knowledge is still quite limited, I worry about how I'm supposed to code something. Not worrying about the implementation, but only thinking about what it is supposed to do is a very refreshing and liberating feeling :) ha, let some code monkey after me refactor and clean up the messy code I left.
User avatar
arjan.top
Forum Contributor
Posts: 305
Joined: Sun Oct 14, 2007 4:36 am
Location: Hoče, Slovenia

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by arjan.top »

DataStoreTest.php

Code: Select all

// ... snip ...
 
  public function testSaveDelegatesToStorageModel() {
    $this->_storageModel->expectOnce('save');
    $this->_dataStore->save();
  }
 
// ... snip ...
 

Code: Select all

 
<?php
 
require_once dirname(__FILE__) . '/StorageModel.php';
 
class DataStore {
 
  private $_store = array();
 
  private $_storageModel;
 
  public function __construct(StorageModel $storageModel) {
    $this->_storageModel = $storageModel;
  }
 
  public function set($key, $value) {
    $this->_store[$key] = $value;
  }
 
  public function get($key) {
    return $this->_store[$key];
  }
 
  public function save() {
    $this->_storageModel->save($this->_store);
  }
 
}
 
User avatar
arjan.top
Forum Contributor
Posts: 305
Joined: Sun Oct 14, 2007 4:36 am
Location: Hoče, Slovenia

Re: TDD workshop. Anybody welcome; Data storage abstraction.

Post by arjan.top »

matthijs wrote:Ok, good to know. Will reread the docs and see if I can find out more.
Here it is:
http://simpletest.sourceforge.net/en/mo ... ation.html
Post Reply