0

I'm developing unit tests on a project and I came across a class that does not contain constructor.

You may ask yourself, "How does this object exist then?"

Well, in another system, this object is generated and saved in database, so in the system where unit tests are required, there is no constructor defined for that class. But there is a need to test their functions so that if they are modified in the system in the future, the tests will indicate in which situations they are used, this will ensure me not to neglect the other points of the project that use it.

Within this environment, how can you test this type of class?

I tried using mocks, but mock brings all null attributes. In addition, a mocked object only comes as a result of a function that you say previously it will bring. So to test the functions of the object itself, this is not functional.

Any idea?

PS: Sorry for the bad english.

3
  • A class does not need a constructor for you to be able to initialise or unittest it. You can still do $testObj = new MyClass(); and then test whether $testObj->someFunction() behaves as expected. Did you run into any specific issues with the class you're trying to test? Commented Apr 20, 2018 at 20:15
  • Yes, suppose that class contain a function that do something with an attribute, the class initialised like you mentioned, will bring me null Commented Apr 20, 2018 at 20:18
  • If you want to test whether a function behaves as expected when a particular attribute has a specific value, you'd need to set that attribute's value yourself anyway, regardless of whether the constructor did anything to it: $testObj->some_attribute = 'some_value'; $this->assertEqual('expectedResult', $testObj->functionThatDoesSomethingWithSomeAttribute()); Commented Apr 20, 2018 at 20:21

1 Answer 1

1

Constructors are optional. A class does not need a constructor for you to be able to instantiate it or test whether its methods are functioning correctly.

As I understand it, you want to test a method which behaves differently depending on a particular property, which would normally be set by a constructor but in your class isn't. That means that in the actual code usage, that property is probably being set directly at some point or there's a different method that sets its value.

In general, for testing these kinds of methods you should always set such a property yourself anyway. The reason for this is simple: a single test should only test one particular method. If you rely on the constructor, your test would be testing the combination of both the constructor and that method. Your test of the method will become dependent on the constructor behaving properly.

Imagine the following:

class Mood {
    public $happy;

    function __construct() {
        $this->happy = true;
    }

    public function greet() {
        if ($this->happy) {
            return 'Hi!';
        } else {
            return 'Go away';
        }
    }
}

Let's say you want to test the behavior of the greet() method:

class MoodTest extends \PHPUnit\Framework\TestCase {
    public function testGreet() {
        $mood = new Mood();
        $this->assertEqual('Hi!', $mood->greet())
    }
}

This test would pass, because we assume the constructor is doing its job and is setting the $happy property to true. But there are two problems here:

  1. We are not testing that the method works properly if $happy is false
  2. We rely on the constructor to set $happy's initial value to true

This means things can change outside our control that would break this specific test, even though the function still works as expected. Business logic may change so that the constructor will set $happy to false initially. Or the developer's logic may change where the constructor disappears entirely and $happy is set in some other way (maybe a setHappy() method is introduced at some point).

To properly test the greet() method, there should be a test for every possible outcome. In this case the method has a single if statement, so there ought to be a test case for both outcomes of that condition:

class MoodTest extends \PHPUnit\Framework\TestCase {
    public function testGreetIfHappy() {
        $mood = new Mood();
        $mood->happy = true;
        $this->assertEqual('Hi!', $mood->greet())
    }

    public function testGreetIfNotHappy() {
        $mood = new Mood();
        $mood->happy = false;
        $this->assertEqual('Go away', $mood->greet())
    }
}

Now, no matter what happens to the constructor or business logic, these tests are testing only the behavior of the greet() method. Whatever else the constructor is doing (even if doesn't exist or does nothing at all) no longer has any effect on the tests of the greet() method.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you very much! It helped a lot!
this is not required - > $mood->happy = true; (line4) -> its a default case
@AlbertoAcuña You’ve missed the point of the question. $mood->happy is true as a side effect of running the constructor. There are situations where the constructor is not executed, for example when instantiating a child class that overrides it and doesn’t call the parent constructor (which could be intentional or a programmer error, your code doesn’t know). If you’re testing a method that depends on the property having a value, you should ensure it has one in your test flow. Method tests should not be dependent on side effects from other methods, even (or especially) constructors.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.