Page 4 of 15

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

Posted: Tue Apr 22, 2008 12:51 pm
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);
    
}
 
 

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

Posted: Tue Apr 22, 2008 12:59 pm
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];
    }
 
}
 
 

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

Posted: Tue Apr 22, 2008 5:44 pm
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 :)

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

Posted: Wed Apr 23, 2008 1:30 am
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];
    }
 
}
 
 

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

Posted: Wed Apr 23, 2008 1:37 am
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];
  }
 
}
?>
 

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

Posted: Wed Apr 23, 2008 2:44 am
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? :)

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

Posted: Wed Apr 23, 2008 3:06 am
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)

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

Posted: Wed Apr 23, 2008 3:40 am
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?

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

Posted: Wed Apr 23, 2008 4:12 am
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(){}

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

Posted: Wed Apr 23, 2008 8:46 pm
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 :)

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

Posted: Thu Apr 24, 2008 1:33 am
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.

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

Posted: Thu Apr 24, 2008 1:41 am
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.

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

Posted: Thu Apr 24, 2008 2:05 am
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.

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

Posted: Thu Apr 24, 2008 4:49 am
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);
  }
 
}
 

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

Posted: Thu Apr 24, 2008 4:53 am
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