Unit Testing XII - Updating the User Class

Updating the User class to pass the new tests


In the previous article in this series, we looked at our new test class, T3_UserErrorMessageTest.class.php, designed to test the User class testing the User class now that it no longer contains the validation code (which we moved to a separate Validator class). In this one, we'll see the new User3.class.php code, which calls the methods of the Validator class to make sure the fields contain valid values.


MODX logo

The User3 Class Code

This class is not all that different from the User2 class. It adds a $validator class variable that contains an instance of the Validator object. Then, in the set() function's validator code, it calls the appropriate method of the Validator object to validate each field.

Here's the code (also available at GitHub here):

<?php

    require_once 'validator.class.php';

    class User3 {
        protected $errors = array();
        protected $validator = null;
        protected $fields = array(
            'username' => '',
            'email' => '',
            'phone' => ''
        );

    function __construct($properties = array()){
        $this->validator = new Validator();
        if (!empty($properties)) {
            foreach ($properties as $key => $value) {
                $this->set($key, $value);
            }
        }
    }

    public function set($fieldName, $value) {
        $errorMsg = '';
        switch($fieldName) {
            case 'username':
                $valid = $this->validateUsername($value);
                $errorMsg = 'Invalid username';
                break;

            case 'email':
                $valid = $this->validateEmail($value);
                $errorMsg = 'Invalid email';
                break;

            case 'phone':
                $valid = $this->validatePhone($value);
                $errorMsg = 'Invalid phone';
                break;

            default:
                $valid = false;
                $errorMsg = 'Unknown field';

        }
        if ($valid === false) {
            $this->addError($errorMsg);
        } else {
            $this->fields[$fieldName] = $value;
        }
        return $valid;
    }
    public function get($fieldName) {
        if (array_key_exists($fieldName, $this->fields)) {
            return $this->fields[$fieldName];
        } else {
            $this->addError('Unknown Field: ' . $fieldName);
            return null;
        }
    }

    public function validateUsername($username) {
        return $this->validator->validateUsername($username);
    }

    public function validateEmail($email) {
        return $this->validator->validateEmail($email);
    }

    public function validatePhone($phone) {
        return $this->validator->validatePhone($phone);
    }

    public function save() {
        return true;
    }

    public function addError($error) {
        $this->errors[] = $error;
    }

    public function getErrors() {
        return $this->errors;
    }
    public function clearErrors() {
        $this->errors = array();
    }

    public function hasErrors() {
        return !empty($this->errors);
    }
}

Design Issues

The code in both the test, and the class, are still not ideal. As I said in the earlier article on the T3_UserErrorMessageTest test, the way we're doing things is useful if you have to test code that you can't edit (such as legacy code or a third-party library). Some would argue that you should never test code you can't change, though sometimes it's impossible to avoid. I presented the examples in the previous articles to illustrate some useful unit test techniques, but in real Test-Driven Development, what we've seen so far would not show good design practices.

For one thing, our test is misusing the concept of stubs. It's a pretty well-accepted principle that you shouldn't test a stubbed class. You should use stubs and mocks to test a class, but not by stubbing or mocking the class itself.

Another issue is the principle that if you're using new in your constructor, you're probably doing something wrong.

Finally, our User class and our Validator class are still too dependent on each other. Technically, they are too "tightly coupled." For example, we load the Validator class in our User class. What if someone moves it? Our test would be dead in the water.

Similarly, what if you find a new third-party validator class that you like better than the one we have? To use it, the User class would have to be rewritten even though it doesn't actually do any validation.

In the next two articles, we'll use an Inversion of Control (IOC) technique called "Dependency Injection" to solve those problems.


Coming Up

In the next two articles in this series, we'll discuss Dependency Injection (DI) and redesign both our test and our User class to be more "loosely coupled."



For more information on how to use MODX to create a web site, see my web site Bob's Guides, or better yet, buy my book: MODX: The Official Guide.

Looking for high-quality, MODX-friendly hosting? As of May 2016, Bob's Guides is hosted at A2 hosting. (More information in the box below.)



Comments (0)


Please login to comment.

  (Login)