views:

443

answers:

1

I'm new to unit-testing, so this is maybe a little dumb question. Imagine, we have a simple model method.

public function get_all_users($uid = false, $params = array()){
    $users = array();
    if(empty($uid) && empty($params)){return $users;}
    $this->db->from('users u');
    if($uid){
        $this->db->where('u.id',(int)$id);
    }
    if(!empty($params)){
       if(isset($params['is_active']){
          $this->db->where('u.status ', 'active');
       }
       if(isset($params['something_else']){ // some more filter actions}
    }
    $q = $this->db->get();
    if($q->num_rows()){
        foreach($q->result_array() as $user){
            $users[$user['id']] = $user;
        }
    }
    $q->free_result();
    return $users;
}

The question is how a _good test would be written for it? UPD: I guess, the best unit-testing library for CI is Toast, so example i'm looking for, preferable be written using it. Thanks.

+2  A: 

I'm using toast too, and mostly I use it to test a model methods. To do it, first truncate all table values, insert a predefined value, then get it. This is the example of test I used in my application:

class Jobads_tests extends Toast
{
  function Jobads_tests()
  {
    parent::Toast(__FILE__);
    // Load any models, libraries etc. you need here
    $this->load->model('jobads_draft_model');
    $this->load->model('jobads_model');
  }

  /**
   * OPTIONAL; Anything in this function will be run before each test
   * Good for doing cleanup: resetting sessions, renewing objects, etc.
   */
  function _pre()
  {
    $this->adodb->Execute("TRUNCATE TABLE `jobads_draft`");
  }

  /**
   * OPTIONAL; Anything in this function will be run after each test
   * I use it for setting $this->message = $this->My_model->getError();
   */
  function _post()
  {
    $this->message = $this->jobads_draft_model->display_errors(' ', '<br/>');
    $this->message .= $this->jobads_model->display_errors(' ', '<br/>');
  }

  /* TESTS BELOW */
  function test_insert_to_draft()
  {
    //default data
    $user_id = 1;

    //test insert
    $data = array(
      'user_id' => $user_id,
      'country' => 'ID',
      'contract_start_date' => strtotime("+1 day"),
      'contract_end_date' => strtotime("+1 week"),
      'last_update' => time()
    );
    $jobads_draft_id = $this->jobads_draft_model->insert_data($data);
    $this->_assert_equals($jobads_draft_id, 1);

    //test update
    $data = array(
      'jobs_detail' => 'jobs_detail',
      'last_update' => time()
    );
    $update_result = $this->jobads_draft_model->update_data($jobads_draft_id, $data);
    $this->_assert_true($update_result);

    //test insert_from_draft
    $payment_data = array(
      'activation_date' => date('Y-m-d', strtotime("+1 day")),
      'duration_amount' => '3',
      'duration_unit' => 'weeks',
      'payment_status' => 'paid',
      'total_charge' => 123.45
    );
    $insert_result = $this->jobads_model->insert_from_draft($jobads_draft_id, $payment_data);
    $this->_assert_true($insert_result);

    //draft now must be empty
    $this->_assert_false($this->jobads_draft_model->get_current_jobads_draft($user_id));

  }
}

I'm using AdoDB in my application, but don't get confuse with that. You can do $this->db inside the test controller, after you load the database library. You can put it in autoload so it will automatically loaded.

See that in my code, before the test is run, the table is truncated. After run, I will get any error that might occured. I do assert for a predefined insert and update. Using Toast to test the model will make you sure that the model's method doing exactly the task that you want it to do. Make the test that you need, and make sure you cover all the possibilities of input and output values.

Donny Kurnia
Cool, but I'm wondering why you place all those tests within a single test function? Why not put them in individual functions - I assume that would make it easier to bugtrack if an assert statement fails?
Jens Roland
In my test above, it reflect the real case in my application. That's why there are 2 'action' in one test. Even if you do this, if one fail, then it will displayed, which is fail, the first or second action.You can make it whatever you want to fit your need.
Donny Kurnia