Unit testing for controllers

I’m building a web application, and wish to follow test-driven development methodology. I find myself adding a significant amount of code in the Model, which has an automatically generated unit test class under ‘protected/tests/unit/’. All was going well, until I decided to add another action to my Controller. That was when I realized that there is no baseline unit testing class for the Controller.

It would be easy to add a class to begin testing my Controller classes, but is this common practice? Is the Controller supposed to be dumb enough where it does not need unit testing (i.e. just forwards function calls to the model after running them through the filter chain)?

Thanks!

Hi,

Yes I’m going more and more into that direction, having light (dumb) controllers and fat models. Better, I recommend relying on a service layer to put the domain logic into. This has the benefit to allow to reuse your services easily without much hassle.

Hth

Can you provide an example of a ‘service layer’? Where would that fit into the MVC pattern? Would this shift a lot of complexity out of the model and into the service layer?

Thanks,

Tom

Tom,

I see services as an API of your application, managing the different models needed to perform a client action. I think the complexity (business logic) of a particular model should stay in the model. Service layer should handle the complexity involved by managing various models to perform an action.

For example, I need to create a person, and store all contact information for this person. I would call my PersonService->createPerson, pass it all the submitted data, and let it handle both the creation of a person, and create the related person contact information if provided:




// in the controller

public function actionCreate()

{

    // using service layer

    $contactService = new ContactService;

    if (!isset($_POST['Contact'])) {

        $person = $contactService->getNewPerson();

    } else {

        $person = $contactService->createPerson($_POST['Contact']);

        if (!$person->hasErrors()) {

            Yii::app()->user->setFlash('contact_created', true);

            $this->redirect(array('view','id'=>$person->id));

        }

    }

    $this->render('create',array(

	'contact'=>$person,

    ));

}




// in the person service

/**

 * Creates a new person

 * @param array attribute values (name=>value) to be set.

 * @return Contact model instance

 */

public function createPerson($data)

{

    $person = new Contact;

    $person->type_id = Contact::TYPE_PERSON;

    $person->attributes = $data;

    if (isset($data['contact_data'])) {

        $contactData = $data['contact_data'];

        $person->setContactData($contactData);

    }

    if ($person->save()) {

        $person->updateContactData();

    }

    

    return $person;

}



Main benefit for me is to be able to reuse this code. E.g. if another component in my application needs to create a Person, or if I want to expose my application through an API.

Like Julien I also use heavy Models and light controllers.

To avoid a model class getting so big it is hard to oversee things I create a base model and for (some) processes I extend this model.

Example:

**** UserModel extends CActiveRecord.

 This is the base model. I define rules which always apply, like the lengths of attributes,


 the type of an attribute (integer, boolean, email), etc.


 Logic like the beforeSave() handles things like encoding the password, filling timestamp fields, ...

**** RegistrationModel extends UserModel.

 This registers a new user. It derives all the core business logic from the userModel, but adds some


 process specific things. It is a like a scenario, only encoded in a separate class.

Interesting examples. I like the idea of the service layer juggling multiple models where necessary – seems to be a better place than the model itself.

In my case, I have a one-to-one relationship between two models. Why not merge them? Because I wish to compartment them – a third party is responsible for one of the models, and I’m responsible for the other.

Instead of embedding the code in a single portion of the model, I could implement a service layer to manage both models. I like this idea and will likely integrate it next time I refactor.