testing.rst 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. .. _testing:
  2. ==================
  3. Testing With UUIDs
  4. ==================
  5. One problem with the use of ``final`` is the inability to create a `mock object`_
  6. to use in tests. However, the following techniques should help with testing.
  7. .. tip::
  8. To learn why ramsey/uuid uses ``final``, take a look at :ref:`faq.final`.
  9. .. _testing.inject:
  10. Inject a UUID of a Specific Type
  11. --------------------------------
  12. Let's say we have a method that uses a type hint for :php:class:`UuidV1
  13. <Ramsey\\Uuid\\Rfc4122\\UuidV1>`.
  14. .. code-block:: php
  15. public function tellTime(UuidV1 $uuid): string
  16. {
  17. return $uuid->getDateTime()->format('Y-m-d H:i:s');
  18. }
  19. Since this method uses UuidV1 as the type hint, we're not able to pass another
  20. object that implements UuidInterface, and we cannot extend or mock UuidV1, so
  21. how do we test this?
  22. One way is to use :php:meth:`Uuid::uuid1() <Ramsey\\Uuid\\Uuid::uuid1>` to
  23. create a regular UuidV1 instance and pass it.
  24. .. code-block:: php
  25. public function testTellTime(): void
  26. {
  27. $uuid = Uuid::uuid1();
  28. $myObj = new MyClass();
  29. $this->assertIsString($myObj->tellTime($uuid));
  30. }
  31. This might satisfy our testing needs if we only want to assert that the method
  32. returns a string. If we want to test for a specific string, we can do that, too,
  33. by generating a UUID ahead of time and using it as a known value.
  34. .. code-block:: php
  35. public function testTellTime(): void
  36. {
  37. // We generated this version 1 UUID ahead of time and know the
  38. // exact date and time it contains, so we can use it to test the
  39. // return value of our method.
  40. $uuid = Uuid::fromString('177ef0d8-6630-11ea-b69a-0242ac130003');
  41. $myObj = new MyClass();
  42. $this->assertSame('2020-03-14 20:12:12', $myObj->tellTime($uuid));
  43. }
  44. .. note::
  45. These examples assume the use of `PHPUnit`_ for tests. The concepts will
  46. work no matter what testing framework you use.
  47. .. _testing.static:
  48. Returning Specific UUIDs From a Static Method
  49. ---------------------------------------------
  50. Sometimes, rather than pass UUIDs as method arguments, we might call the static
  51. methods on the Uuid class from inside the method we want to test. This can be
  52. tricky to test.
  53. .. code-block:: php
  54. public function tellTime(): string
  55. {
  56. $uuid = Uuid::uuid1();
  57. return $uuid->getDateTime()->format('Y-m-d H:i:s');
  58. }
  59. We can call this in a test and assert that it returns a string, but we can't
  60. return a specific UUID value from the static method call --- or can we?
  61. We can do this by :ref:`overriding the default factory <customize.factory>`.
  62. First, we create our own factory class for testing. In this example, we extend
  63. UuidFactory, but you may create your own separate factory class for testing, as
  64. long as you implement :php:interface:`Ramsey\\Uuid\\UuidFactoryInterface`.
  65. .. code-block:: php
  66. namespace MyPackage;
  67. use Ramsey\Uuid\UuidFactory;
  68. use Ramsey\Uuid\UuidInterface;
  69. class MyTestUuidFactory extends UuidFactory
  70. {
  71. public $uuid1;
  72. public function uuid1($node = null, ?int $clockSeq = null): UuidInterface
  73. {
  74. return $this->uuid1;
  75. }
  76. }
  77. Now, from our tests, we can replace the default factory with our new factory,
  78. and we can even change the value returned by the :php:meth:`uuid1()
  79. <Ramsey\\Uuid\\UuidFactoryInterface::uuid1>` method for our tests.
  80. .. code-block:: php
  81. /**
  82. * @runInSeparateProcess
  83. * @preserveGlobalState disabled
  84. */
  85. public function testTellTime(): void
  86. {
  87. $factory = new MyTestUuidFactory();
  88. Uuid::setFactory($factory);
  89. $myObj = new MyClass();
  90. $factory->uuid1 = Uuid::fromString('177ef0d8-6630-11ea-b69a-0242ac130003');
  91. $this->assertSame('2020-03-14 20:12:12', $myObj->tellTime());
  92. $factory->uuid1 = Uuid::fromString('13814000-1dd2-11b2-9669-00007ffffffe');
  93. $this->assertSame('1970-01-01 00:00:00', $myObj->tellTime());
  94. }
  95. .. attention::
  96. The factory is a static property on the Uuid class. By replacing it like
  97. this, all uses of the Uuid class after this point will continue to use the
  98. new factory. This is why we must run the test in a separate process.
  99. Otherwise, this could cause other tests to fail.
  100. Running tests in separate processes can significantly slow down your tests,
  101. so try to use this technique sparingly, and if possible, pass your
  102. dependencies to your objects, rather than creating (or fetching them) from
  103. within. This makes testing easier.
  104. .. _testing.mock:
  105. Mocking UuidInterface
  106. ---------------------
  107. Another technique for testing with UUIDs is to mock
  108. :php:interface:`UuidInterface <Ramsey\\Uuid\\UuidInterface>`.
  109. Consider a method that accepts a UuidInterface.
  110. .. code-block:: php
  111. public function tellTime(UuidInterface $uuid): string
  112. {
  113. return $uuid->getDateTime()->format('Y-m-d H:i:s');
  114. }
  115. We can mock UuidInterface, passing that mocked value into this method. Then, we
  116. can make assertions about what methods were called on the mock object. In the
  117. following example test, we don't care whether the return value matches an
  118. actual date format. What we care about is that the methods on the UuidInterface
  119. object were called.
  120. .. code-block:: php
  121. public function testTellTime(): void
  122. {
  123. $dateTime = Mockery::mock(DateTime::class);
  124. $dateTime->expects()->format('Y-m-d H:i:s')->andReturn('a test date');
  125. $uuid = Mockery::mock(UuidInterface::class, [
  126. 'getDateTime' => $dateTime,
  127. ]);
  128. $myObj = new MyClass();
  129. $this->assertSame('a test date', $myObj->tellTime($uuid));
  130. }
  131. .. note::
  132. One of my favorite mocking libraries is `Mockery`_, so that's what I use in
  133. these examples. However, other mocking libraries exist, and PHPUnit provides
  134. built-in mocking capabilities.
  135. .. _mock object: https://en.wikipedia.org/wiki/Mock_object
  136. .. _PHPUnit: https://phpunit.de
  137. .. _Mockery: https://github.com/mockery/mockery