5

I am trying to find out the best, or correct, way to check if a class has a constant defined with PHPUnit. The PHPUnit docs don't seem to cover this, which makes me wonder if I am doing the correct thing by testing this - however it is an important feature of my class.

I have the following class:

PurchaseManager.php

/**
 * Message sent when a course has been purchased
 */
const COURSE_PURCHASED_MESSAGE = 'coursePurchasedMessage';

...and part of it's test class has this test:

PurchaseManagerTest.php

public function testCoursePurchasedMessageConstant()
{
    $pm = new PurchaseManager();
    $this->assertTrue(defined(get_class($pm) . '::COURSE_PURCHASED_MESSAGE'));
}

Is this correct? It passes, but i'm just interested to know if this is accurate and best practice.

I am using PHPUnit 5.0.8.

crmpicco
  • 16,605
  • 26
  • 134
  • 210

2 Answers2

11

I'm using Reflection class for this purpose. It has getConstants method which returns an associative array [<constant_name> => <constant_value>, ...].

Something like:

public function testHasSiteExportedConstant()
{
    $mailer = new \ReflectionClass(SiteExporter::class);
    $this->assertArrayHasKey('SITE_EXPORTED', $mailer->getConstants());
}
crmpicco
  • 16,605
  • 26
  • 134
  • 210
BVengerov
  • 2,947
  • 1
  • 18
  • 32
  • Thanks. What is the advantage of using Reflection over my approach? – crmpicco Sep 02 '16 at 07:21
  • 1
    For simple cases both approaches are good, but for more complex tests reflection methods are easier to use, read and maintain, just because they don't require additional manipulations (like `defined`, `get_class`, concatenation, `::`). – BVengerov Sep 02 '16 at 07:36
  • 3
    a slightly nicer, because more direct way, would be `$this->assertTrue($mailer->hasConstant('SITE_EXPORTED')`. – Adrian Föder Jun 20 '18 at 09:09
5

I would never test for the existence of a constant, attribute, or method. Unless you are testing a code generator, of course.

Sebastian Bergmann
  • 7,837
  • 1
  • 27
  • 35
  • Thanks for the comment, there was something inside me telling me I shouldn't do it for some reason and i'm not sure why. I have written tests with other tools such as PhpSpec and have checked for the existence of classes then. The constant is an important part of my class and if it is missing/changed then i'd like to know about it, however are you saying that is not the correct usage of PHPUnit? – crmpicco Sep 02 '16 at 07:54
  • 3
    Can you explain why? – MonkeyMonkey Mar 24 '17 at 21:44
  • 1
    If using unit tests to also form a pseudo 'contract' with what your code publicly surfaces to other code, and that other code uses that constant, and it changes, it's good that it gets caught by the unit test, so that you know your 'contract' also changed. Maybe unit tests are the wrong place for it. `¯\_(ツ)_/¯` – gingerCodeNinja Sep 13 '17 at 14:42
  • I would argue that almost no complex projects can do without some occasional (and hopefully temporary) hacks which are intended to cover design flaws. And such hacks require better test coverage exactly because of being out of ordinary and not obvious. E.g. being dependent on existence of some constant :-) – BVengerov Oct 12 '17 at 10:03
  • 3
    I agree with Sebastian in regards to that you should not test inner workings of things, just "interfaces", i.e. the output behavior given on particular input. However, I came here whether to test the existence of a public constant (which is bad, I know, but it's how it is); and since it's public, it IS somehow considerable an interface and therefore I test it. – Adrian Föder Jun 20 '18 at 09:01