3

Well, I'm new to unit-testing (with phpUnit) and just started to test one class of mine.

Actual constructor looks like this:

/**
 * Loads configuration.
 */
function __construct() {

    $config =
        Kohana::$config->load('koffee');

    $this->_table_name = $config->table_name;
    $this->_table_columns = $config->table_columns;

}

It basically gets configuration from another file and sets it as protected properties to that object.

Here is how unit-test looks (it's not finished and that's where I need help):

/**
 * Tests that config is loaded and correct.
 */
function testConfigIsLoadedAndCorrect() {

    $object = new Model_Article();

    $config = Kohana::$config->load('koffee');

    // Compare object's **protected** properties to local `$config`. How?!

}

The problem is that properties are protected and I cannot access them so easy...

Possible solutions I see at the moment:

  1. Change visibility of properties (I don't like this),
  2. Add, so called, "getters" to class I test - not unit-test (I don't like this neither);

Probably this is funny to you, but, as I said, I'm new to unit-tests. Any help much appreciated.

daGrevis
  • 21,014
  • 37
  • 100
  • 139
  • You have not written *what* you actually want to test and *why* you think there is a problem. – hakre Dec 27 '11 at 18:59
  • I want to compare object's **protected** properties to local `$config` as written in the test. – daGrevis Dec 27 '11 at 19:06

4 Answers4

8

Unit-testing is about unit testing. Protected members are not part of the public interface of a unit, which is all you should need to care about when writing unit tests.

You don't test the inner guts of a unit, but that it works as expected.

If you regardless of that want to do such stuff, you can use Serialization­Docs, casting to array and Reflection­Docs to inspect protected/private properties of an object or to execute protected/private methods of an object.


See as well: PhpUnit private method testingSO Q&A

Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836
  • 2
    (*related*): [`Stackoverflow: phpunit-private-method-testing`](http://stackoverflow.com/questions/5937845/phpunit-private-method-testing/5937899#5937899) - Same general answer, some more code samples and motiviation. +1 – edorian Dec 27 '11 at 21:42
  • @edorian: Thanks for leaving the link, your superb PHPUnit knowledge is much appreciated. – hakre Jan 03 '12 at 09:54
2

A technique I've used in the past has been to create a Tester class that exposes the appropriate methods to test the object. In this case, Model_Article_Tester would inherit Model_Article and expose a get method. The benefit here is you expose what you need for tests without affecting production code.

Ahmish
  • 1,151
  • 8
  • 8
2

You could create a subclass that does expose the data you need and only use it for unit testing.

You could also test the behavior of the class rather than the data. The Model_Article must do something with the table_name and table_columns so test that behavior. For example, if the Model_Article is used to create an html table, then you could set the config values, create the Model_Article, use it to create the html, then assert that it matches a hard coded string like <table title='name'><tr><th>col1</th><th>col2</th</tr></table>

[edit] You could also rely on constructor injection to pass the table_name and table_columns instead of having a hidden dependency on the configuration.

Trystan Spangler
  • 1,685
  • 11
  • 20
  • Can you give me an example of constructor injection, please? – daGrevis Dec 27 '11 at 19:13
  • 1
    All parameters needed in the constructor are parameters of the constructor: `function __construct($table_name, $table_columns)`. A creator service can then instantiate the needed class with the configuration injected w/o needing the class to know how configuration is done and the creator to know which options are available for configuration. – hakre Dec 27 '11 at 19:17
  • This means that I need to load that config separately from the class and then just pass it to constructor (`new Model_Article('x', 'y')`)? – daGrevis Dec 27 '11 at 19:21
  • In the real world code yes, that's probably what you will do. But in the unit tests, you can pass your own values and make sure that you are only testing that the Model_Article works as you expect and you don't have to worry about the configuration. This also makes the Model_Article more predictable since you know it only relies on its inputs and not hidden values. – Trystan Spangler Dec 27 '11 at 20:07
0

You can try to use ReflectionClass::newInstanceWithoutConstructor

$reflector = new ReflectionClass('Model_Article');
$object = $reflector->newInstanceWithoutConstructor();
Adlaran
  • 155
  • 2
  • 7