37

I get this error:

1) XTest::testX
array_merge(): Argument #1 is not an array

ERRORS!
Tests: 1, Assertions: 0, Errors: 1.

On this test case:

use PHPUnit\Framework\TestCase;

class XTest extends TestCase
{

    function __construct()
    {}

    function testX()
    {
        $this->assertTrue(true);
    }
}

If I remove __construct method, my tests pass. What is going on with PHPUnit's handling of my class constructor methods? It worked fine in PHPUnit version 4.8, but now I am using PHPUnit version 6.1.3

Dennis
  • 7,907
  • 11
  • 65
  • 115
  • 4
    what if you call `parent::__construct();` ? The signature can be found here https://github.com/sebastianbergmann/phpunit/blob/6.1.3/src/Framework/TestCase.php#L328 – Sander Visser May 03 '17 at 14:22
  • interestingly, it will not throw error if I use `parent::__construct();` – Dennis May 03 '17 at 14:23
  • You are better off using the `setUp()` methods for initialization. Further more you can get a stacktrace when your run phpunit with the `-v` verbose flag. – Sander Visser May 03 '17 at 14:25
  • How would you use `setUp()` for initialization? Sometimes I have classes that use `__construct()` method legitimately. How would you use `setUp` on them?... – Dennis May 03 '17 at 14:27
  • `setUp` is automatically called by phpunit after your constructor is called. You can find more information about other method here : https://phpunit.de/manual/current/en/fixtures.html – Sander Visser May 03 '17 at 14:28
  • You can't override a method that requires an argument with one that doesn't (well theoretically you could, but you never should) – GordonM May 03 '17 at 14:34
  • thanks, thus perhaps I can rename my `__constructor` to say `setUp` for any initialization tasks, although in my case I need to initialize it once per class, while `setUp` does it once per test case – Dennis May 03 '17 at 14:36
  • 1
    You can also use `setUpBeforeClass` which is called really once. `setUp` is called for every specified test in your class. – Sander Visser May 03 '17 at 14:38
  • also just to note, using `-v` option did not uncover any more info in my case, it gave the same error message, and i.e. did not give me the offending line where it crashed. I had to sleuth this one out. I am curious though that if I use `parent::__construct()`, still without any parameters, it did work fine – Dennis May 03 '17 at 14:40
  • The verbose flag doesn't work you're right – Sander Visser May 03 '17 at 14:48
  • It happens because the constructor of the TestCase sets `$this->data = []` – Sander Visser May 03 '17 at 14:49
  • There is no reason to implement `__construct()` in a `TestCase`. Use `setUp()` for initialization. It is invoked before each test. – axiac Jul 27 '17 at 17:33

2 Answers2

57

PHPUnit uses the constructor for initialization of the base TestCase

You can see the constructor method here: https://github.com/sebastianbergmann/phpunit/blob/6.1.3/src/Framework/TestCase.php#L328

public function __construct($name = null, array $data = [], $dataName = '')

You shouldn't use the constructor, because it's used by phpunit and any change to the signature etc can break things.

You can use the special setUp and setUpBeforeClass methods which phpunit will call for you.

use PHPUnit\Framework\TestCase;

class XTest extends TestCase
{
    function static setUpBeforeClass()
    { 
       // Called once just like normal constructor
       // You can create database connections here etc
    }

    function setUp()
    {
      //Initialize the test case
      //Called for every defined test
    }

    function testX()
    {
        $this->assertTrue(true);
    }

    // Clean up the test case, called for every defined test
    public function tearDown() { }

    // Clean up the whole test class
    public static function tearDownAfterClass() { }
}

The docs: https://phpunit.de/manual/current/en/fixtures.html

Note that the setUp gets called for every specified test in the class.

For a single initialization you can use setUpBeforeClass.

And another tip: run your phpunit with the -v flag to display stack traces ;)

Sander Visser
  • 4,144
  • 1
  • 31
  • 42
  • 1
    it looks like `setUpBeforeClass` needs to be static to work properly, while `setUp` does not need to be – Dennis May 03 '17 at 14:49
  • 2
    No, using the constructor is not a solution. Use `setUp()`. – Sebastian Bergmann May 03 '17 at 15:24
  • 1
    Hey sebastian could you explain that a bit? I know it's bad practice but not sure the reasons behind it. – Sander Visser May 03 '17 at 15:29
  • 1
    The constructor "belongs" to PHPUnit. `setUp()` is the extension point for users. – Sebastian Bergmann May 03 '17 at 15:30
  • So by overwriting the `__constructor` you should follow the exact same constructor signature defined by phpunit. Therefor it's dis encouraged because users can easily break things – Sander Visser May 03 '17 at 15:33
  • 3
    @SebastianBergmann I find it logical. However since it did work in PHPUnit 4.8, it would be very nice to see a bit more descriptive error message than ``array_merge(): Argument #1 is not an array``. Maybe a hint like ``setUp() should be used instead of __construct()``. – Juha Untinen Jul 27 '17 at 22:26
  • @SebastianBergmann Using setUp or setUpBeforeClass seems a not complete solution because they are static classes. No way to init data like $this->myvarforothermethod = .... Adding parent::__construct(); in custom constructor solve the trouble for me and make my tests compatible with phpunit v6.1 and lower. – eldy Sep 02 '18 at 11:59
20

As Sander Visser's answer correctly pointed, the parent constructor might have additional parameters, etc, and generally you'd want to use setUpBeforeClass or setUp, but, if you know what you're doing, you can call parent::__construct(); in the constructor of your Test class:

public function __construct() {
    parent::__construct();
    // Your construct here
}

Edit 2019

In Codeception, this can also be caused by an invalid YML indentation on your suite file.

Lucas Bustamante
  • 15,821
  • 7
  • 92
  • 86