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
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 »

Here's the latest files (including above test) by the way. Sorry, should have posted them before.
Attachments
data_store.zip
(136.92 KiB) Downloaded 187 times
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 »

test for multidimensional array:

Code: Select all

 
  public function testSavingMultipleValuesPassesMultidimensionalArray() {
    $storageModel = $this->_createStorageModel();
    $storageModel->expectOnce('save', array( array(
      'foo' => array(
        'bar' => array(
          'zip' => 'button'
          )
        ),
      'foo2' => array(
        'bar2' => 'button2'
          )
        )
    ));
    $dataStore = $this->_createDataStore($storageModel);
    $dataStore->set('foo/bar/zip', 'button');
    $dataStore->set('foo2/bar2', 'button2');
    $dataStore->save();
  }
 
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: Definitely worth further explanation. We all need to be clear what we're doing. In my opinion, save() should accept an array in exactly the same format as the array provided by load(). When that array is modified (by set() or remove()) the array passed to save() should reflect that modification.

We don't care what happens to the internal array ($this->_store) when set() or remove() are called. We don't even care that such an internal array exists. What we do care about is that save() receives integral data.
Sorry, this confuses me only more. First, how does save() accept an array if it doesn't have any function parameters?

Second, why don't we care about the internal array?

I thought that:
- instantiating the DataStore will load the StorageModel.
- now the private $_store is the array of data having been loaded
- by calling set($var,$value) or remove($key) we manipulate that $_store array.
- calling save() put that array $_store back in the StorageModel.
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 »

Still passes. Looking good :)

Test for load() followed by save().

Code: Select all

 public function testSavingUnmodifedData() {
    $storageModel = $this->_createStorageModel();
    $storageModel->setReturnValue('load', array('foo' => 'bar'));
    $storageModel->expectOnce('save', array( array('foo' => 'bar') );
    $dataStore = $this->_createDataStore($storageModel);
    $dataStore->save();
  }
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 »

save does have $data parameter:

Code: Select all

 
interface StorageModel {
  public function save($data);
  public function load();
}
 

EDIT:
I see now, we are testing StorageModel save() not DataStore save()

EDIT2:
wrong, we are not testing save() we are just testing parameters of save basically ...
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 »

now it would pass :D (bracket missing)

Code: Select all

 
  public function testSavingUnmodifedData() {
    $storageModel = $this->_createStorageModel();
    $storageModel->setReturnValue('load', array('foo' => 'bar'));
    $storageModel->expectOnce('save', array( array('foo' => 'bar')) );
    $dataStore = $this->_createDataStore($storageModel);
    $dataStore->save();
  }
 
Chris | Thanks, spotted then when a ran the test ;)
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:Sorry, this confuses me only more. First, how does save() accept an array if it doesn't have any function parameters?
StorageModel->save() has a parameter. DataStore->save() doesn't though ;) DataStore tells StorageModel what to save. Look at the way we do $this->_storageModel->save($this->_store) ;)
matthijs wrote:Second, why don't we care about the internal array?
Because it's not part of the public API. Setting and getting values we care about. We care that values go into and back out correctly. What *is* part of the public API is $storageModel->save() though. We're about to to start testing load() and save() on a real StorageModel soon so we need to know that DataStore is doing the same thing our tests will be doing.
- instantiating the DataStore will load the StorageModel.
Correct. We care that it asks the StorageModel for its data. But we don't care what it does with it. It could stick it in a database for all we care. It could use a comma delimited string. It could use a little object known only to itself. We don't care. We just care that the interfaces we've written are being used correctly :)
- now the private $_store is the array of data having been loaded
In our implementation yes. This has nothing to do with the behaviour of our class from an outside point of view though.
- by calling set($var,$value) or remove($key) we manipulate that $_store array.
Again, in our implementation yes.
- calling save() put that array $_store back in the StorageModel.
In our implementation yes. But the only bit we care about on the public side of things is that $storageModel is given data in a known format. Since we've written an interface for StorageModel we have specified a way to extend the DataStore. A developer needs to know that a StorageModel he implements will be given a multidimensional array. So as a result, we need to test that this is indeed the case. if we had no test to show that's what our code does then how can somebody write a StorageModel implementation? :D
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

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

Post by matthijs »

Ok, you guys lost me.

I'll reread some of the posts and try to get it.
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:Ok, you guys lost me.

I'll reread some of the posts and try to get it.
I get the feeling it may be that you're getting mixed up with the difference between DataStore and StorageModel? :)

In terms of the whole debate about not caring about $_store. Just look at it this way. All we care about are the methods we declare as "public function ...".
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

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

Post by matthijs »

But we are still testing datastore right? Not storagemodel? And the datastore class doesn't have an argument in the save() method. I thought that calling
$datastore->save() just takes the current state of $_store and saves that to the storagemodel.
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:But we are still testing datastore right? Not storagemodel?
Correct. But DataStore passes an argument to StorageModel. It would be all well and good to ignore this fact and just write tests for StorageModel later on, passing in "ideal" values for the sake of testing, but we need to know that DataStore is also passing in ideal values.
And the datastore class doesn't have an argument in the save() method. I thought that calling
$datastore->save() just takes the current state of $_store and saves that to the storagemodel.
Bingo! You basically just described what we need to test, and what we have just written the last two tests for ;) If you read our last two tests that got posted they not testing anything given to $dataStore->save(), they are testing what $dataStore gives to $storageModel->save().

$storageModel is a mock object in our test case. Mock objects are able to make expectations about arguments which get passed to their methods. As you describe in the above quote "$datastore->save() just takes the current state of $_store and saves that to the storagemodel". Does it? Let's test it to make sure then :)
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

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

Post by matthijs »

Ok, so as we can't test what is in $_store (it's private), we test if what is in $datastore->save and what is been sent to $storage_model->save is correct.

I think I get it. I was being confused by the talk about arguments for save().
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:Ok, so as we can't test what is in $_store (it's private), we test if what is in $datastore->save and what is been sent to $storage_model->save is correct.

I think I get it. I was being confused by the talk about arguments for save().
Correct :)

I really want people to understand why though :)

Imagine when we create our XmlStorageModel and we want it to generate some XML. It will have to be called something like this:

Code: Select all

$xml = new XmlStorageModel('/some/xml/file.xml');
$xml->save(array('foo' => 'bar'));
So if that's how a storage model will read data, that's how DataStore *must* give it to the StorageModel. We'd have a big gaping hole in our test coverage if we didn't cover this point ;)

Unrelated to this discussion, I fear I'm about to confuse people even more, since the next thing we're going to test is that specification we had regarding being able to read data in one format and write it in another :P
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

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

Post by matthijs »

Ok, but with the 3 tests so far

Code: Select all

 
  public function testSavingSingleValuePassesSimpleArray() {
    $storageModel = $this->_createStorageModel();
    $storageModel->expectOnce('save', array( array('zip' => 'button') ));
    $dataStore = $this->_createDataStore($storageModel);
    $dataStore->set('zip', 'button');
    $dataStore->save();
  }
 
 
  public function testSavingMultipleValuesPassesMultidimensionalArray() {
    $storageModel = $this->_createStorageModel();
    $storageModel->expectOnce('save', array( array(
      'foo' => array(
        'bar' => array(
          'zip' => 'button'
          )
        ),
      'foo2' => array(
        'bar2' => 'button2'
          )
        )
    ));
    $dataStore = $this->_createDataStore($storageModel);
    $dataStore->set('foo/bar/zip', 'button');
    $dataStore->set('foo2/bar2', 'button2');
    $dataStore->save();
  }
 
 
  public function testSavingUnmodifedData() {
    $storageModel = $this->_createStorageModel();
    $storageModel->setReturnValue('load', array('foo' => 'bar'));
    $storageModel->expectOnce('save', array( array('foo' => 'bar')) );
    $dataStore = $this->_createDataStore($storageModel);
    $dataStore->save();
  }
 
What is still missing then? Should we feed load() a completely different format?

This is getting more difficult then I'd have thought. But maybe it's good to use the few brain cells that are left once in a while.. :)
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 »

Nope, let's not touch load(). It's working fine :) save() is working fine too as our previous tests just showed :)

But just to add some confusion how I envisaged saving in a different format was like this:

Code: Select all

$dataStore = new DataStore($modelA);
 
$dataStore->save($modelB);
Now I *have* added a paramater to DataStore->save(), but that's for this special case. It will be an optional parameter which simply allow the data to be saved in a different model. Imagine if our concrete models were XmlModel and YamlModel then we could do:

Code: Select all

$dataStore = new DataStore(new XmlModel('file.xml'));
$dataStore->set('whatever', 'xyz');
 
$dataStore->save(new YamlModel('file.yml'));
So rather than saving to the XML file, we can direct the persistent data elsewhere.

This is a point for us to think about. Is this interface logical? Can we think of any better alternatives? :)
Post Reply