2

I am trying to run a group of unit tests after reading an example here which require an active database connection. It seems to work for one unit test, but it will fail with multiple tests due to redefinition of a constant contained in a Laravel Command class. Here are the errors I receive after running PHP Unit:

PHPUnit 3.7.31 by Sebastian Bergmann.

FEE

Time: 203 ms, Memory: 5.00Mb

There were 2 errors:

1) MessageControllerTest::testSixMonths
ErrorException: Constant SHARE_SEND_EXPIRATION already defined

src/app/commands/DeleteExpiredSends.php:31
src/app/start/artisan.php:14
src/vendor/laravel/framework/src/Illuminate/Console/start.php:57
src/vendor/laravel/framework/src/Illuminate/Console/Application.php:30
src/vendor/laravel/framework/src/Illuminate/Foundation/Artisan.php:70
src/vendor/laravel/framework/src/Illuminate/Foundation/Artisan.php:45
src/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:206
src/app/tests/MessageControllerTest.php:46
src/app/tests/MessageControllerTest.php:46
src/app/tests/MessageControllerTest.php:21

2) MessageControllerTest::testOneYear
ErrorException: Constant SHARE_SEND_EXPIRATION already defined

src/app/commands/DeleteExpiredSends.php:31
src/app/start/artisan.php:14
src/vendor/laravel/framework/src/Illuminate/Console/start.php:57
src/vendor/laravel/framework/src/Illuminate/Console/Application.php:30
src/vendor/laravel/framework/src/Illuminate/Foundation/Artisan.php:70
src/vendor/laravel/framework/src/Illuminate/Foundation/Artisan.php:45
src/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:206
src/app/tests/MessageControllerTest.php:46
src/app/tests/MessageControllerTest.php:46
src/app/tests/MessageControllerTest.php:21

FAILURES!
Tests: 3, Assertions: 1, Errors: 2.

And here is my unit test:

<?php

class MessageControllerTest extends TestCase
{
    /**
     * setUp() : set up the unit tests by initializing the database
     */
    public function setUp()
    {
        // call the parent's setup method
        parent::setUp();

        // init the DB
        $this->migrateDatabase();
    }

    /**
     * createApplication() : bootstrap the app in order to execute
     * tests
     *
     * @return Symfony\Component\HttpKernel\HttpKernelInterface  
     */
    public function createApplication()
    {
        // set the unitTesting flag to true
        $unitTesting = true;

        // set the environment variable to 'testing'
        $testEnvironment = 'testing';

        return require __DIR__.'/../../bootstrap/start.php';
    }

    /**
     * migrateDatabase() : load the sqlite database into memory
     */
    private function migrateDatabase()
    {
        Artisan::call('migrate');
    }

    /**
     * testHundredFilesMessage() : tests to see if custom message will
     * return after 100 messages have been sent by the same user
     */
    public function testHundredFilesMessage()
    {
        // TODO
        $this->assertTrue(false);
    }

    /**
     * testSixMonthMessage() : test the six months message
     */
    public function testSixMonthMessage()
    {
        // TODO
        $this->assertTrue(false);
    }

    /**
     * testOneYearMessage() : test the one-year message
     */
    public function testOneYearMessage()
    {
        // TODO
        $this->assertTrue(false);
    }
}

The first test seems to pass, but all subsequent tests throw errors. I am guessing that somehow the createApplication() method gets called several times which might explain why the Command class DeleteExpiredSends gets redefined; the class shows a define statement in its constructor:

/**
 * Create a new command instance.
 *
 * @return void
 */
public function __construct()
{
    parent::__construct();
    define("SHARE_SEND_EXPIRATION", 14);
}

If anyone could tell me how to avoid redefinition for this unit test, so as to avoid modifying the internals of the Command class, it would be most helpful. It only seems to be an issue where this unit test is concerned.

5
  • Why are you using define for your constant? Of course you get that error if that class is instantiated more than once. Commented Feb 12, 2014 at 21:14
  • I understand, however my implicit question was more along the lines of "why is it getting defined more than once, and is there a way I can avoid multiple definitions?". As for why define was used, that code was written by another developer and I am not entirely sure about the specifics of that particular API. I amended my question above. Commented Feb 12, 2014 at 21:25
  • Also, I suspect that if there are defines elsewhere in the application, I will run into the same problem even after eliminating the define in question. Commented Feb 12, 2014 at 21:29
  • 1
    I think I found the issue, but I am not quite sure how to get around it yet: github.com/laravel/framework/issues/1798 Commented Feb 12, 2014 at 21:38
  • 1
    Looks like the code needs refactoring to be testable. Workarounds aren't the answer. You can define those constants outside of classes but in the end you need to change them to class constants. Unittests also exist to show flaws in code. Commented Feb 12, 2014 at 21:48

3 Answers 3

4

For each test method add two annotations for phpunit, like this:

/* @runInSeparateProcess
 * @preserveGlobalState disabled
 */
public function testPageShow()
{
  • runInSeparateProcess : run each test in a separate process
  • preserveGlobalState disabled : disable global state from previous test
Sign up to request clarification or add additional context in comments.

Comments

1

I think for temporary solution while you are still not sure on where to put those global constants you have, make use of defined instead.

defined('SAMPLE_GLOBAL_CONSTANT')
  or define('SAMPLE_GLOBAL_CONSTANT', 'sample value of the constant')

And by the way, in case you also run on redefinition exception for a global function you can also use of function_exists method.

But be sure to make use of Class instead to hold that global functions so you can easily test your application. Like put it on something like Helper or much more specific like StringHelper.

Comments

0

Using SenseException's solution to remove all define statements (it turns out there were only two used in the whole application) and instead use class constants did resolve the issue.

Comments

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.