Archive: July, 2012

CakePHP + MongoDB, Next Steps

CakePHP 2.2 / MongoDB 2.0.4

Let’s continue building on top of what we’ve done previously.

I’d say that the main consideration when it comes to building out your MongoDB is the choice between embedding everything into a single collection vs linking (or manual referencing) to another collection.

What does it mean?

If we take our blog example, we saw how easy it was to build a tiny app, which allows to add posts. No need to create any tables (collections) or define any sort of schema. Just get the data and insert.
Now we are ready to move forward and our application would probably require an ability to add comments. Because MongoDB is schema-less, first thought would be to store the Post and Comments into a single document. After all, this is the beauty of NoSQL, we don’t need any JOIN’s, we don’t have to worry about belongsTo or hasMany. Again, a simple head-on approach would be: get the data and insert it into the collection of documents (i.e. Posts and Comments are stored together, aka “embedding”).

This is all fine and dandy, and will likely work quite well for our simple example.
However, this post would be useless if we didn’t consider some other issues. For instance, it would become a bit more difficult (actually it can become very difficult in a more complex application) to single out a specific comment. Perhaps you need to edit one comment out of a hundred or so. Having Comments embedded with the Posts, will require a bit of hacking around. (I strongly urge you to read this wonderful post, which details some these considerations: http://seanhess.github.com/2012/02/01/mongodb_relational.html).

So, for the sake of the example (at least), we’ll separate our collections into something more RDBMS-like and create Post and Comment separately (aka “linking” or “manual referencing”).
As you probably suspect we’ll use the same approach as we would in a traditional SQL DB, and create a reference field in the Comments collection… post_id anyone?

Side note: the referencing that we are doing here is called “manual referencing” in MongoDB terms. There is also an option to use DBRefs, which a slightly more advanced approach. In most cases, and such as the one we have here, “manual referencing” is the recommended way to structure data. Also, at the time of writing PHP’s MongoDB driver does not yet support DBRefs, it is however in the works.

With all this mind let’s take a look at our CakePHP view, app/View/Posts/view.ctp:

<?php if (!empty($post)) : ?>
  <h3>
    <?php echo h($post['Post']['title']); ?>
  </h3>
  <p>
    <?php echo h($post['Post']['body']); ?>
  </p>

  <?php if (!empty($comments)) : ?>
    <?php foreach($comments as $comment) : ?>
      <p>
          <?php echo h($comment['Comment']['user']); ?>
      </p>
      <p>
          <?php echo h($comment['Comment']['body']); ?>
      </p>
    <?php endforeach; ?>
  <?php endif; ?>

  <?php
    echo $this->Form->create(array(
      'url' => array(
        'controller' => 'comments',
        'action' => 'add'
      )
    ));
    echo $this->Form->inputs(array(
        'legend' => 'Add some comments:',
        'Comment.user',
        'Comment.body'
        )
    );
    echo $this->Form->input('Comment.post_id', array(
        'type' => 'hidden',
        'value' => $post['Post']['_id']
    ));
    echo $this->Form->end('Add comment');
  ?>
<?php endif; ?>

Nothing different here from our typical CakePHP view. The page will display a post, some comments (if available) and a form to add more comments.
As evident from the form, we should create a Comments Controller with an add() action. To allow the saving of the comments.

Let’s do so right now:

<?php
class CommentsController extends AppController {
       
public function add() {
        if ($this->request->is('post')) {
            if ($this->Comment->save($this->request->data)) {
                $this->Session->setFlash('Your comment has been saved.');
                return $this->redirect(array(
                    'controller' => 'posts',
                    'action' => 'view',
                    $this->request->data['Comment']['post_id']
                ));
            } else {
                $this->Session->setFlash('Unable to add your post.');
            }
        }
    }
}

This is all that we would need in order to start saving Comments and their link (via post_id) to the actual Post. Again, since we are dealing with a schema-less DB there is no need to worry about creating anything on the DB layer. Our Comment model is also not required, because we don’t have any validation logic or custom methods, so an App Model instance, which is created by cake for us is sufficient for this basic operation.

Now, that the comments are saved we should also display them on the “view post” page. If you take a look at the view above, you’ll see that we’ve already setup the logic to display the comments and we’d expect a $comments data array to do so.

Here’s our view() method in the relevant Posts Controller:

public function view($id = null) {
      $this->Post->id = $id;
      $this->set('post', $this->Post->read());
      $this->set('comments', ClassRegistry::init('Comment')->find('all', array(
          'conditions' => array(
            'post_id' => $id
          )
      )));
    }

Whoa… what happened to find(‘all’)? Why did I just issue two separate queries, and had to instantiate a Comment model like that?
Before you despair, let’s summarize a few things:

  1. We did not setup any models, so our relationship between Post and Comment is unknown.
  2. Currently the MongoDB driver does not support relationships as you would expect from a typical RDBMS. This is not to discourage you form the beauty of using an ORM. On the contrary, MongoDB is not meant to have deep-linking and complex relationships between collections. Remember, MongoDB is all about de-normalization. (That being said, there’s no reason why cake can’t support a basic one-level association, the driver is simply not there yet).
  3. Considering the above, this is not to say that we don’t need models at all. I always mention that all business logic should be tucked away in the model as much as possible. So this is not an excuse to make our controllers fat. The example is simple enough, where extra code would be wasteful.

I’m going to wrap things up at this point, as this should already give you some interesting ideas and food for thought. You’ll notice that I’ve embedded the Poster’s name with the Comment. Does that mean that I would take the approach of storing all user data with the Comments? No, not really. Here it is just a display name, which can be derived from the form or a session if we had logged-in users in the system.
Further considerations and examples of this will be a good topic for another day.

CakePHP + MongoDB Introduction

CakePHP 2.2/MongoDB 2.0.4

We all know that CakePHP is awesome, and I’m sure you’ve heard that MongoDB is pretty awesome as well.
So how do we make these awesome technologies play well together?

In an awesomely easy way :)

Let’s get things rolling by setting up MongoDB.
(Instructions for Ubuntu)

sudo pecl install mongo

Add the following line to your php.ini file (on my system in: /etc/php5/apache2/php.ini)

extension=mongo.so

This sets up PHP support for MongoDB.

Let’s install the actual DB, which is easily available through apt-get

sudo apt-get install mongodb mongodb-server

Restart things:

sudo /etc/init.d/apache2 restart

… and double-check that mongo is up and running.

mongo --version

You should see something like: “MongoDB shell version: 2.0.4″

Now, we’ll grab the MongoDB driver for CakePHP, courtesy of Yasushi Ichikawa (ichikaway).

First, cd into your app/Plugin directory.
Then:

sudo git clone git://github.com/ichikaway/cakephp-mongodb.git Mongodb

Once the cloning is complete you should cd into Mongodb directory and:

git checkout cake2.0

Now you have all the components installed and running, and it’s time to try out a simple app.
There is certain beauty in MongoDB, because it doesn’t require you to have any schema. Just write some code, and things will happen magically.

Indeed we’ll just edit our app/Config/database.php

public $default = array(
    'datasource' => 'Mongodb.MongodbSource',
    'database' => 'blog',
    'host' => 'localhost',
    'port' => 27017,
  );

Add to app/Config/bootstrap.php

CakePlugin::load('Mongodb');

Create a model:

<?php
class Post extends AppModel {
 
  public $validate = array(
    'title' => array(
        'rule' => 'notEmpty'
    ),
    'body' => array(
        'rule' => 'notEmpty'
    )
  ); 
}

Controller:

<?php
class PostsController extends AppController {
   
    public function index() {
        $this->set('posts', $this->Post->find('all'));
    }
     
    public function add() {
        if ($this->request->is('post')) {
            if ($this->Post->save($this->request->data)) {
                $this->Session->setFlash('Your post has been saved.');
                return $this->redirect(array('action' => 'index'));
            } else {
                $this->Session->setFlash('Unable to add your post.');
            }
        }
    }
}

And a couple of views…

app/View/index.ctp

<?php
  foreach ($posts as $post) {
    debug($post);
  }

app/View/add.ctp

<?php

echo $this->Form->create();
echo $this->Form->inputs(array(
    'title', 'body'
));
echo $this->Form->end('Add');

Congratulations! Your first CakePHP/MongoDB app is now ready to rock.

You can go ahead and try adding some posts, and see them appear (albeit only as debug output) on the index action.

Building “the blog tutorial”… the TDD way (part 2 – controller testing)

CakePHP 2.2

We have prepped our app with enough testing, to be ready to move on to write out the rest of the code…
There was a good reason why we have started our TDD with the model layer. By encapsulating the business logic into our models we were able to test our application quite well, without having to write any controller code.

Now, feeling a lot more confident, let’s go ahead and create app/Controller/PostsController.php:

<?php
class PostsController extends AppController {
   
    public function index() {
        $this->set('posts', $this->Post->getAllPosts());
    }
}

We already know that our getAllPosts() method is working well, because it’s been tested in the model. Therefore our controller test will simply re-enforce that knowledge and complete the cycle by making sure our expectation of variables set() for the view matches the return. One simple example of where this could be an issue is a heavy and complex Post model, which has a ton of methods.
It is not that far-fetched to write:

$this->set('posts', $this->Post->getEachPost());

Perhaps, we’ve inherited the app from another developer or simply weren’t sure what getEachPost() method does, or made the wrong presumption that this is the method we need to be using for the index action. Thus, creating a simple test case can secure out intent and expectation on the controller level.

app/Test/Case/Controller/PostsControllerTest.php:

<?php
class PostsControllerTest extends ControllerTestCase {
    public $fixtures = array('app.post');
       
    public function testIndex() {
      $this->testAction('/posts/index');
      $this->assertInternalType('array', $this->vars['posts']);
      $expected = array(
          array(
              'id' => 2,
              'title' => 'A title once again'
          )
      );
      $result = Hash::extract($this->vars['posts'], '{n}.Post[id=2]');
      $this->assertEquals($expected, $result);
     
    }

As you see we’ve used the same fixture as we did in part 1, while testing our Post Model. Here, we have leveraged the index() action of the Posts Controller and made sure that our record with ID = 2, matches our expected return from the Post Model and sets the correct output to be used in the view. You’ll notice the use of $this->vars, which is simply a CakePHP way of storing the view variables within the testing environment.
The two assertions we have tested for:
1. That we have some array of variables ready for display (i.e. our Posts).
2. That a record with ID = 2, is the Post.id and Post.title, which, as we know, ultimately comes from our Post Fixture.
Once again, we have now bridged our test to cover everything from the model layer to controller to the setting of the view variables.

Our view() action and test are not going to be much different, and since we are well familiar with what our output and expectations should be, let’s add the missing bits to move on with our application development.

First we’ll add the new action to our Posts Controller:

public function view($id = null) {
      $this->set('post', $this->Post->getSinglePost($id));
    }

And the test case:

public function testView() {
      $this->testAction('/posts/view/3');
      $this->assertInternalType('array', $this->vars['post']);
      $expected = array(
          'Post' => array(
            'id' => '3',
            'title' => 'Title strikes back',
            'body' => 'This is really exciting! Not.',
            'created' => '2012-07-04 10:43:23',
            'updated' => '2012-07-04 10:45:31'
          )
      );
      $this->assertEquals($expected, $this->vars['post']);
    }

By now you should be armed with enough knowledge to figure out this simple addition to our app on your own.

Now we can get into something a little more interesting and take a look at how we would use Mock Objects to test our add() action.
This is a nice show-off of the simplicity of such a powerful feature and how nicely it is integrated in CakePHP. Sometimes we don’t need or don’t want to have a full-blown app written just to test bits of logic (as the case might be with the simple add() method in our little app). Therefore cake will generate “fake” objects (i.e. Mock Objects) and leverage some, but not all, of the existing code to do our testing.

Let’s beef up the Posts Controller with the new add() action:

public function add() {
        if ($this->request->is('post')) {
            if ($this->Post->addPost($this->request->data)) {
                $this->Session->setFlash('Your post has been saved.');
                return $this->redirect(array('action' => 'index'));
            } else {
                $this->Session->setFlash('Unable to add your post.');
            }
        }
    }

… and let’s take a look at the test case:

public function testAddViaMock() {
      $postData = array(
          'Post' => array(
            'title' => 'New Post Title',
            'body' => 'TDD FTW!'
          )
      );
           
      $this->testAction('/posts/add', array(
          'data' => $postData,
          'method' => 'post'
          )
      );
     
      $this->assertContains('http://app/posts', $this->headers['Location']);
      $this->assertEquals(4, $this->controller->Post->find('count'));    
    }
   
public function testAddViaMockWithBadData() {
      $postBadData = array(
          'Post' => array(
            'title' => '',
            'body' => 'TDD FTW!'
          )
      );
     
      $this->testAction('/posts/add', array(
          'data' => $postBadData,
          'method' => 'post'
          )
      );
           
      $this->assertTrue(!empty($this->controller->Post->validationErrors));
      $this->assertContains('This field cannot be left blank', $this->controller->Post->validationErrors['title']);
      $this->assertEquals(3, $this->controller->Post->find('count'));
      $this->assertTrue(empty($this->headers));
    }

As you see we’ve actually added two methods to test some assertions. One comes with “good” data and one with “bad” data (empty title). This helps us to keep things more granular and allows to test our if/else conditions all the way to saving of the data and model validation.

Behind the scenes cake will Mock a controller object for us, meaning it created a “fake” Posts Controller. Although in reality the framework still leverages the existing code, yet only minimal parts are required for testing. There is an option to fine-tune the creation of such Mock Objects by using the generate() method, but in this case we can get away by relying on the already created model, fixture and bits of the controller.
I mentioned before that models are easier to test and that it is always a good idea to keep the logic in the model, hopefully you can start to see why. Had we placed all the logic in the controller the testing would become a lot more complicated and at the same time not nearly as precise as what we have now.
It does take a little a practice to write good test cases. However, by employing the TDD way of thinking we are naturally pushed towards making better decisions about logic separation and thereby necessity forces us to improve the overall architecture.

I will let you browse the code and take a look at the assertions, there really isn’t anything new there, that we have not covered yet. Just to summarize:
1. We test user input with good data, and make sure our if/else and redirection works as expected. Hint: $this->assertContains(‘http://app/posts’, $this->headers['Location']);. (Yep, the post was saved, therefore we got redirected back to “index”).
2. We make sure our new post is indeed saved as expected: $this->assertEquals(4, $this->controller->Post->find(‘count’));
3. In the second method we ensure that validation will kick-in, if a user forgets to add a post title.
4. And double-check that no data will be stored in this case.

This post is already getting rather long, so I will not bore you anymore by showing the edit() action and the test case for it. In reality it would be nearly identical to the add() action, and since we’ve seen these methods tested on the model layer it shouldn’t take you long to build up a test case on your own.

Building “the blog tutorial”… the TDD way (part 1 – model testing)

CakePHP 2.2

I personally consider Test Driven Development (TDD) as a “must do”, rather than a “nice to have”. Unfortunately as developers we often suffer from tight deadlines, tons of requirements and deliverables and sometimes a simple lack of understanding as to why TDD is so important.
Granted, TDD requires certain time and effort to be spent before “real” development begins (I am willing to argue this point), therefore unit testing becomes an afterthought. This is rather sad, because in the long run it will save you time, frustration and money. No longer will you have litter your application code with debug statements or try to trace logic of an application because something unforeseen and inexplicable is suddenly happening.

PHPUnit manual (a de facto standard in PHP unit testing and the testing framework leveraged by CakePHP) puts it quite nicely:

The sooner you test for a mistake the greater your chance of finding it and the less it will cost to find and fix. This explains why leaving testing until just before releasing software is so problematic. Most errors do not get caught at all, and the cost of fixing the ones you do catch is so high that you have to perform triage with the errors because you just cannot afford to fix them all.

Let’s take our favorite blog tutorial and approach it the TDD way.
I will presume that you are familiar with CakePHP and have read about testing within CakePHP environment already, and have setup your app to be ready for testing (i.e. installed PHPUnit, setup test DB… and possibly installed Xdebug. For details please refer to the CakePHP manual in the link above).

If we follow the blog tutorial, you’ll see that first we will be creating a Post Model. Since we are doing things via TDD, this sort of translates into the idea that we’ll need a Post Fixture. You could work with the live data instead, but since we are just getting started fixtures is a straight-forward and solid approach to unit testing.

So, app/Test/Fixture/PostFixture.php:

<?php
class PostFixture extends CakeTestFixture {
      public $fields = array(
          'id' => array('type' => 'integer', 'key' => 'primary'),
          'title' => array('type' => 'string', 'length' => 255, 'null' => false),
          'body' => 'text',
          'created' => 'datetime',
          'updated' => 'datetime'
      );
      public $records = array(
          array('id' => 1, 'title' => 'The title', 'body' => 'This is the post body.', 'created' => '2012-07-04 10:39:23', 'updated' => '2012-07-04 10:41:31'),
          array('id' => 2, 'title' => 'A title once again', 'body' => 'And the post body follows.', 'created' => '2012-07-04 10:41:23', 'updated' => '2012-07-04 10:43:31'),
          array('id' => 3, 'title' => 'Title strikes back', 'body' => 'This is really exciting! Not.', 'created' => '2012-07-04 10:43:23', 'updated' => '2012-07-04 10:45:31')
      );
 }

We’ll keep things simple and create the fixture with some test records, just like the blog tutorial suggests.
If you’ve read and tried the blog tutorial already, you’ll see that all of the logic is really contained in the controller. Additionally all the heavy lifting is delegated to the core methods such as save() and find().
It would be silly to test these core methods (as cake developers have already done a great job at that), therefore I am going to create wrapper methods in the model, so that we have something to test. First, because models are generally easier to test than controllers, and secondly because even in such a simple app we can yet again benefit from the golden rule of MVC “fat models, skinny controllers”.
Let us modify the code just a little to see what I mean.
Instead of writing out the controller just yet, I am going to create a method in our Post Model to get Post.id and Post.title for our soon-to-be-created index view.
(Since we’ve read the blog tutorial, we are aware of the expectations and can therefore approach our application with a TDD mindset).

app/Model/Post.php

<?php
class Post extends AppModel {

  public function getAllPosts() {
    return $this->find('all', array(
        'fields' => array('id', 'title')
    ));
  }
}

Our model now has one method, which I can test to be sure that my expectation of how it should behave matches the reality.
Thus, comes our first test app/Test/Case/Model/PostTest.php:

<?php
App::uses('Post', 'Model');

class PostTest extends CakeTestCase {
    public $fixtures = array('app.post');

    public function setUp() {
        parent::setUp();
        $this->Post = ClassRegistry::init('Post');
    }

    public function testGetAllPosts() {
        $result = $this->Post->getAllPosts();
        $expected = array(
            array('Post' => array('id' => 1, 'title' => 'The title')),
            array('Post' => array('id' => 2, 'title' => 'A title once again')),
            array('Post' => array('id' => 3, 'title' => 'Title strikes back'))
        );

        $this->assertEquals($expected, $result);
    }
}

If we execute our test, the following output should be shown: “1/1 test methods complete: 1 passes, 0 fails, 1 assertions and 0 exceptions.” (If Xdebug is installed the coverage should be at 100%, w00t!).

This is all good news… but what have we accomplished?

First of all, without having to populate our application table with any testing data we were able to make sure that our getAllPosts(); method is working as expected, thanks to our Post Fixture. Secondly, I am quite confident now that once I start writing the controller code I will not run into any trouble.
Pfft… all this coding just to make sure that “The title” which I wrote in the fixture matches my expectation of “The title” in test case. Well, yeah. Yet, imagine a slightly more complicated app. I might already have some data in the DB, and now by using TDD and fixtures adding a new method to the model will not require me to add fake data to the table, mess up my pretty code with debug statements and so on and so forth.
Just like tight coupling is generally bad for your system architecture, coupling testing and production code is just as bad (if not worse) and wasteful. Also, this sets us up for a solid foundation and proper mindset for further growth and easy development.

Let us move forward, now that we have prepared our model/method to build an index() action.
What comes next? Eventually, we would like to build a view() action, which is going to display a single post based on a given $id. With our TDD mindset, let us extend our Post Model and Post Test.

First we’ll add a new method to our Post Model

public function getSinglePost($id = null) {
    return $this->find('first', array(
        'conditions' => array('id' => $id)
    ));
  }

If you’ve been around CakePHP for a little while, chances are this method is going to work perfectly well. Although after years of development I can definitely misspell “conditinos” (ooops!). I am not taking any chances, not when my work comes down to 0 and 1 eventually, and therefore I will be sure to write a test case for it. If we were to analyze our code coverage at this point we only have tests for 62.5% of the application… bad developer.

Let’s add a test for the new method:

public function testGetSinglePost() {
        $result = $this->Post->getSinglePost(2);               
        $expected = array(
            'Post' => array(
                'id' => 2,
                'title' => 'A title once again',
                'body' => 'And the post body follows.',
                'created' => '2012-07-04 10:41:23',
                'updated' => '2012-07-04 10:43:31'
            )
        );
        $this->assertEquals($expected, $result);

Once again, if we were to run to the test the output should show that our app is at 100% coverage and that our method is indeed returning the record where ID = 2. For the sake of discussion try to write “condition” instead of “conditions” in our Post Model. The test immediately fails.
Yet, without unit testing I could’ve clicked on the link in the browser to see my post and possibly not even realize that cake returned the wrong record, in such case it would simply return the first record (clicked a link, saw a post, quickly move to the next step). Thus a perfect example of how a simple bug can slip by a developer, especially after hours of coding. Also, by testing in small increments I can quickly narrow down my attention to the specific method, and hopefully catch my mistake quicker. We all know that such issues become hard to spot after coding for hours and jumping all over the app.

Great, we are now ready to setup our add() method.

Let’s extend the Post Model even further:

public function addPost($postData) {
    return $this->save($postData);
  }

And let’s add a new test for this rather simple method:

public function testAddPost() {
      $postData = array(
          'title' => 'Test Post Title',
          'body' => 'We love TDD. Yeah!',
          'created' => '2012-07-04 10:43:23',
          'updated' => '2012-07-04 10:45:31'
      );
     
      $numRecordsBefore = $this->Post->find('count');
      $result = $this->Post->addPost($postData);
      $numRecordsAfter = $this->Post->find('count');
     
      $expected = array(
            'Post' => array(
                'id' => 4,
                'title' => 'Test Post Title',
                'body' => 'We love TDD. Yeah!',
                'created' => '2012-07-04 10:43:23',
                'updated' => '2012-07-04 10:45:31'
            )
        );
     
      $this->assertEquals(4, $numRecordsAfter);
      $this->assertTrue($numRecordsBefore != $numRecordsAfter);
      $this->assertEquals($expected, $result);
    }

Essentially we are just re-testing the save() method, but there are some interesting things happening here nonetheless. I know that my original record-set contained 3 rows, I would like to verify that by counting the number of rows before and after the save() operation. Because I am saving a single record from my $postData array, my expectation would be to now have four records.
So, as you see, in a single test I can make a number of “assertions” to really bullet-proof my expectations.
1. I expect the number of records to increase by one.
2. The number of records before and after the save() operation should not be the same.
3. My saved record will have a new ID, which is equal to 4 (since our primary key column is auto-incremented). Notice the difference between my data array and my expected array, which now has an ‘id’ column.
This demonstrates how a very simple method can be thoroughly tested using multiple assertions, so that I can peacefully sleep at night knowing that my code is working exactly as I expect it too.

Last, but not least, we have an edit() method to worry about. Let us proceed with creating an editPost() method and a relevant test case.

In app/Model/Post.php, we’ll add:

public function editPost($postData) {
    return $this->save($postData);
  }

And our test case:

public function testEditPost() {
      $this->Post->id = 3;
      $postData = array(
          'title' => 'Test Post Title. Updated.',
          'body' => 'We love TDD. Yeah! Yeah!',
          'created' => '2012-07-04 10:43:23',
          'updated' => '2012-07-04 10:49:51'
      );
      $recordBeforeEdit = $this->Post->read();
      $numRecordsBefore = $this->Post->find('count');
      $result = $this->Post->editPost($postData);
      $numRecordsAfter = $this->Post->find('count');
     
      $expected = array(
            'Post' => array(
                'id' => 3,
                'title' => 'Test Post Title. Updated.',
                'body' => 'We love TDD. Yeah! Yeah!',
                'created' => '2012-07-04 10:43:23',
                'updated' => '2012-07-04 10:49:51'
            )
        );
     
      $this->assertEquals($expected, $result);
      $this->assertTrue($numRecordsBefore == $numRecordsAfter);
           
      $recordCompare = array_diff($recordBeforeEdit['Post'], $result['Post']);
      $expectedArrayDiffResult = array(
        'title' => 'Title strikes back',
        'body' => 'This is really exciting! Not.',
        'updated' => '2012-07-04 10:45:31'
      );
     
      $this->assertEquals($expectedArrayDiffResult, $recordCompare);
    }

Whoa, again we have a rather simple method and rather thorough test with some important expectations. In this case we are modifying our record with ID = 3.
1. Since we are presuming an SQL UPDATE, our expected result should contain ‘id’ => 3 (as evident from the $expected array).
2. We need to be certain that, unlike in our addPost() operation, in this case the number of records remains the same.
3. Comparing the existing record to an updated one, we should have “title”, “body” and “updated” fields changed, while our “id” and “created” fields remain the same. We verify this by using PHP’s array_diff() and comparing our expectation to the actual output.

What about data validation?

Surely we can test that as well. Let’s add validation rules to our Post Model, just like suggested in the blog tutorial.

public $validate = array(
    'title' => array(
        'rule' => 'notEmpty'
    ),
    'body' => array(
        'rule' => 'notEmpty'
    )
  );

And a test case, to seal the deal:

public function testValidation() {
      $postData = array(
          'title' => '',
          'body' => 'Oh no, this post has and empty title!'
      );
      $result = $this->Post->addPost($postData);
      $invalidFields = $this->Post->invalidFields();
 
      $this->assertFalse($result);
      $this->assertContains('This field cannot be left blank', $invalidFields['title']);
     
      $postData = array(
          'title' => 'No body in the post? Impossible.',
          'body' => ''
      );
      $result = $this->Post->addPost($postData);
      $invalidFields = $this->Post->invalidFields();
     
      $this->assertFalse($result);
      $this->assertContains('This field cannot be left blank', $invalidFields['body']);
     
      $postData = array(
          'title' => 'Title...',
          'body' => '... and body.'
      );
      $result = $this->Post->addPost($postData);
      $invalidFields = $this->Post->invalidFields();
     
      $this->assertFalse(empty($result));
      $this->assertTrue(empty($invalidFields));
    }

First we are trying to save a post with an empty “title”, which should not be allowed. We prove that by asserting that our $result will be false. We will also verify that the proper validation error was triggered for the given field, “title”. We do exactly the same for the “body” of the post.
Of course, we should also make sure that given some “title” and some “body” the post will be saved, otherwise we could not be certain that our validation rules are not preventing a perfectly valid data to be saved at all. In this case we could compare $result to the actual expected array, but we have done so already while testing the addPost() method. Just a couple of simple false/true assertion will solidify our testing scenarios.
In theory, and this is something I would recommend for a more complex case, we could break up our testValidation() method into testEmptyTitle(), testEmptyBody(), testGoodTitleBody() methods.

In summary, our business logic of the application has been thoroughly tested, we can list multiple posts, we can view a single post, we can add and edit posts and we’ve made sure that our validation rules are working as expected.
Now I am content to commit my code and continue working on the application or pass it onto front-end engineers to build views or another developer to work on controllers. Talk about taking good care of your teammates and taking good care of your coding. Also, we didn’t even write a single line of code in the controllers or the views, but we can be certain that the logic of the application is quite solid. Do you see how TDD is playing so well with MVC paradigm of keeping our business logic in the Model?

In the next part we will continue building and putting finishing touches onto our awesome TDD app.