StubTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. <?php
  2. require_once __DIR__ .'/ResetMocks.php';
  3. use Codeception\Stub;
  4. class StubTest extends \PHPUnit\Framework\TestCase
  5. {
  6. use ResetMocks;
  7. /**
  8. * @var DummyClass
  9. */
  10. protected $dummy;
  11. public function setUp(): void
  12. {
  13. require_once $file = __DIR__. '/_data/DummyOverloadableClass.php';
  14. require_once $file = __DIR__. '/_data/DummyClass.php';
  15. $this->dummy = new DummyClass(true);
  16. }
  17. public function testMakeEmpty()
  18. {
  19. $dummy = Stub::makeEmpty('DummyClass');
  20. $this->assertInstanceOf('DummyClass', $dummy);
  21. $this->assertTrue(method_exists($dummy, 'helloWorld'));
  22. $this->assertNull($dummy->helloWorld());
  23. }
  24. public function testMakeEmptyMethodReplaced()
  25. {
  26. $dummy = Stub::makeEmpty('DummyClass', array('helloWorld' => function () {
  27. return 'good bye world';
  28. }));
  29. $this->assertMethodReplaced($dummy);
  30. }
  31. public function testMakeEmptyMethodSimplyReplaced()
  32. {
  33. $dummy = Stub::makeEmpty('DummyClass', array('helloWorld' => 'good bye world'));
  34. $this->assertMethodReplaced($dummy);
  35. }
  36. public function testMakeEmptyExcept()
  37. {
  38. $dummy = Stub::makeEmptyExcept('DummyClass', 'helloWorld');
  39. $this->assertEquals($this->dummy->helloWorld(), $dummy->helloWorld());
  40. $this->assertNull($dummy->goodByeWorld());
  41. }
  42. public function testMakeEmptyExceptPropertyReplaced()
  43. {
  44. $dummy = Stub::makeEmptyExcept('DummyClass', 'getCheckMe', array('checkMe' => 'checked!'));
  45. $this->assertEquals('checked!', $dummy->getCheckMe());
  46. }
  47. public function testMakeEmptyExceptMagicalPropertyReplaced()
  48. {
  49. $dummy = Stub::makeEmptyExcept('DummyClass', 'getCheckMeToo', array('checkMeToo' => 'checked!'));
  50. $this->assertEquals('checked!', $dummy->getCheckMeToo());
  51. }
  52. public function testFactory()
  53. {
  54. $dummies = Stub::factory('DummyClass', 2);
  55. $this->assertCount(2, $dummies);
  56. $this->assertInstanceOf('DummyClass', $dummies[0]);
  57. }
  58. public function testMake()
  59. {
  60. $dummy = Stub::make('DummyClass', array('goodByeWorld' => function () {
  61. return 'hello world';
  62. }));
  63. $this->assertEquals($this->dummy->helloWorld(), $dummy->helloWorld());
  64. $this->assertEquals("hello world", $dummy->goodByeWorld());
  65. }
  66. public function testMakeMethodReplaced()
  67. {
  68. $dummy = Stub::make('DummyClass', array('helloWorld' => function () {
  69. return 'good bye world';
  70. }));
  71. $this->assertMethodReplaced($dummy);
  72. }
  73. public function testMakeWithMagicalPropertiesReplaced()
  74. {
  75. $dummy = Stub::make('DummyClass', array('checkMeToo' => 'checked!'));
  76. $this->assertEquals('checked!', $dummy->checkMeToo);
  77. }
  78. public function testMakeMethodSimplyReplaced()
  79. {
  80. $dummy = Stub::make('DummyClass', array('helloWorld' => 'good bye world'));
  81. $this->assertMethodReplaced($dummy);
  82. }
  83. public function testCopy()
  84. {
  85. $dummy = Stub::copy($this->dummy, array('checkMe' => 'checked!'));
  86. $this->assertEquals('checked!', $dummy->getCheckMe());
  87. $dummy = Stub::copy($this->dummy, array('checkMeToo' => 'checked!'));
  88. $this->assertEquals('checked!', $dummy->getCheckMeToo());
  89. }
  90. public function testConstruct()
  91. {
  92. $dummy = Stub::construct('DummyClass', array('checkMe' => 'checked!'));
  93. $this->assertEquals('constructed: checked!', $dummy->getCheckMe());
  94. $dummy = Stub::construct(
  95. 'DummyClass',
  96. array('checkMe' => 'checked!'),
  97. array('targetMethod' => function () {
  98. return false;
  99. })
  100. );
  101. $this->assertEquals('constructed: checked!', $dummy->getCheckMe());
  102. $this->assertEquals(false, $dummy->targetMethod());
  103. }
  104. public function testConstructMethodReplaced()
  105. {
  106. $dummy = Stub::construct(
  107. 'DummyClass',
  108. array(),
  109. array('helloWorld' => function () {
  110. return 'good bye world';
  111. })
  112. );
  113. $this->assertMethodReplaced($dummy);
  114. }
  115. public function testConstructMethodSimplyReplaced()
  116. {
  117. $dummy = Stub::make('DummyClass', array('helloWorld' => 'good bye world'));
  118. $this->assertMethodReplaced($dummy);
  119. }
  120. public function testConstructEmpty()
  121. {
  122. $dummy = Stub::constructEmpty('DummyClass', array('checkMe' => 'checked!'));
  123. $this->assertNull($dummy->getCheckMe());
  124. }
  125. public function testConstructEmptyExcept()
  126. {
  127. $dummy = Stub::constructEmptyExcept('DummyClass', 'getCheckMe', array('checkMe' => 'checked!'));
  128. $this->assertNull($dummy->targetMethod());
  129. $this->assertEquals('constructed: checked!', $dummy->getCheckMe());
  130. }
  131. public function testUpdate()
  132. {
  133. $dummy = Stub::construct('DummyClass');
  134. Stub::update($dummy, array('checkMe' => 'done'));
  135. $this->assertEquals('done', $dummy->getCheckMe());
  136. Stub::update($dummy, array('checkMeToo' => 'done'));
  137. $this->assertEquals('done', $dummy->getCheckMeToo());
  138. }
  139. public function testStubsFromObject()
  140. {
  141. $dummy = Stub::make(new \DummyClass());
  142. $this->assertInstanceOf(
  143. '\PHPUnit\Framework\MockObject\MockObject',
  144. $dummy
  145. );
  146. $dummy = Stub::make(new \DummyOverloadableClass());
  147. $this->assertObjectHasAttribute('__mocked', $dummy);
  148. $dummy = Stub::makeEmpty(new \DummyClass());
  149. $this->assertInstanceOf(
  150. '\PHPUnit\Framework\MockObject\MockObject',
  151. $dummy
  152. );
  153. $dummy = Stub::makeEmpty(new \DummyOverloadableClass());
  154. $this->assertObjectHasAttribute('__mocked', $dummy);
  155. $dummy = Stub::makeEmptyExcept(new \DummyClass(), 'helloWorld');
  156. $this->assertInstanceOf(
  157. '\PHPUnit\Framework\MockObject\MockObject',
  158. $dummy
  159. );
  160. $dummy = Stub::makeEmptyExcept(new \DummyOverloadableClass(), 'helloWorld');
  161. $this->assertObjectHasAttribute('__mocked', $dummy);
  162. $dummy = Stub::construct(new \DummyClass());
  163. $this->assertInstanceOf(
  164. '\PHPUnit\Framework\MockObject\MockObject',
  165. $dummy
  166. );
  167. $dummy = Stub::construct(new \DummyOverloadableClass());
  168. $this->assertObjectHasAttribute('__mocked', $dummy);
  169. $dummy = Stub::constructEmpty(new \DummyClass());
  170. $this->assertInstanceOf(
  171. '\PHPUnit\Framework\MockObject\MockObject',
  172. $dummy
  173. );
  174. $dummy = Stub::constructEmpty(new \DummyOverloadableClass());
  175. $this->assertObjectHasAttribute('__mocked', $dummy);
  176. $dummy = Stub::constructEmptyExcept(new \DummyClass(), 'helloWorld');
  177. $this->assertInstanceOf(
  178. '\PHPUnit\Framework\MockObject\MockObject',
  179. $dummy
  180. );
  181. $dummy = Stub::constructEmptyExcept(new \DummyOverloadableClass(), 'helloWorld');
  182. $this->assertObjectHasAttribute('__mocked', $dummy);
  183. }
  184. protected function assertMethodReplaced($dummy)
  185. {
  186. $this->assertTrue(method_exists($dummy, 'helloWorld'));
  187. $this->assertNotEquals($this->dummy->helloWorld(), $dummy->helloWorld());
  188. $this->assertEquals($dummy->helloWorld(), 'good bye world');
  189. }
  190. public static function matcherAndFailMessageProvider()
  191. {
  192. return array(
  193. array(Stub\Expected::atLeastOnce(),
  194. 'Expected invocation at least once but it never'
  195. ),
  196. array(Stub\Expected::once(),
  197. 'Method was expected to be called 1 times, actually called 0 times.'
  198. ),
  199. array(Stub\Expected::exactly(1),
  200. 'Method was expected to be called 1 times, actually called 0 times.'
  201. ),
  202. array(Stub\Expected::exactly(3),
  203. 'Method was expected to be called 3 times, actually called 0 times.'
  204. ),
  205. );
  206. }
  207. /**
  208. * @dataProvider matcherAndFailMessageProvider
  209. */
  210. public function testExpectedMethodIsCalledFail($stubMarshaler, $failMessage)
  211. {
  212. $mock = Stub::makeEmptyExcept('DummyClass', 'call', array('targetMethod' => $stubMarshaler), $this);
  213. $mock->goodByeWorld();
  214. try {
  215. $mock->__phpunit_verify();
  216. $this->fail('Expected exception');
  217. } catch (\Exception $e) {
  218. $this->assertTrue(strpos($failMessage, $e->getMessage()) >= 0, 'String contains');
  219. }
  220. $this->resetMockObjects();
  221. }
  222. public function testNeverExpectedMethodIsCalledFail()
  223. {
  224. $mock = Stub::makeEmptyExcept('DummyClass', 'call', array('targetMethod' => Stub\Expected::never()), $this);
  225. $mock->goodByeWorld();
  226. try {
  227. $mock->call();
  228. } catch (\Exception $e) {
  229. $this->assertTrue(strpos('was not expected to be called', $e->getMessage()) >= 0, 'String contains');
  230. }
  231. $this->resetMockObjects();
  232. }
  233. public static function matcherProvider()
  234. {
  235. return array(
  236. array(0, Stub\Expected::never()),
  237. array(1, Stub\Expected::once()),
  238. array(2, Stub\Expected::atLeastOnce()),
  239. array(3, Stub\Expected::exactly(3)),
  240. array(1, Stub\Expected::once(function () {
  241. return true;
  242. }), true),
  243. array(2, Stub\Expected::atLeastOnce(function () {
  244. return array();
  245. }), array()),
  246. array(1, Stub\Expected::exactly(1, function () {
  247. return null;
  248. }), null),
  249. array(1, Stub\Expected::exactly(1, function () {
  250. return 'hello world!';
  251. }), 'hello world!'),
  252. array(1, Stub\Expected::exactly(1, 'hello world!'), 'hello world!'),
  253. );
  254. }
  255. /**
  256. * @dataProvider matcherProvider
  257. */
  258. public function testMethodMatcherWithMake($count, $matcher, $expected = false)
  259. {
  260. $dummy = Stub::make('DummyClass', array('goodByeWorld' => $matcher), $this);
  261. $this->repeatCall($count, array($dummy, 'goodByeWorld'), $expected);
  262. }
  263. /**
  264. * @dataProvider matcherProvider
  265. */
  266. public function testMethodMatcherWithMakeEmpty($count, $matcher)
  267. {
  268. $dummy = Stub::makeEmpty('DummyClass', array('goodByeWorld' => $matcher), $this);
  269. $this->repeatCall($count, array($dummy, 'goodByeWorld'));
  270. }
  271. /**
  272. * @dataProvider matcherProvider
  273. */
  274. public function testMethodMatcherWithMakeEmptyExcept($count, $matcher)
  275. {
  276. $dummy = Stub::makeEmptyExcept('DummyClass', 'getCheckMe', array('goodByeWorld' => $matcher), $this);
  277. $this->repeatCall($count, array($dummy, 'goodByeWorld'));
  278. }
  279. /**
  280. * @dataProvider matcherProvider
  281. */
  282. public function testMethodMatcherWithConstruct($count, $matcher)
  283. {
  284. $dummy = Stub::construct('DummyClass', array(), array('goodByeWorld' => $matcher), $this);
  285. $this->repeatCall($count, array($dummy, 'goodByeWorld'));
  286. }
  287. /**
  288. * @dataProvider matcherProvider
  289. */
  290. public function testMethodMatcherWithConstructEmpty($count, $matcher)
  291. {
  292. $dummy = Stub::constructEmpty('DummyClass', array(), array('goodByeWorld' => $matcher), $this);
  293. $this->repeatCall($count, array($dummy, 'goodByeWorld'));
  294. }
  295. /**
  296. * @dataProvider matcherProvider
  297. */
  298. public function testMethodMatcherWithConstructEmptyExcept($count, $matcher)
  299. {
  300. $dummy = Stub::constructEmptyExcept(
  301. 'DummyClass',
  302. 'getCheckMe',
  303. array(),
  304. array('goodByeWorld' => $matcher),
  305. $this
  306. );
  307. $this->repeatCall($count, array($dummy, 'goodByeWorld'));
  308. }
  309. private function repeatCall($count, $callable, $expected = false)
  310. {
  311. for ($i = 0; $i < $count; $i++) {
  312. $actual = call_user_func($callable);
  313. if ($expected) {
  314. $this->assertEquals($expected, $actual);
  315. }
  316. }
  317. }
  318. public function testConsecutive()
  319. {
  320. $dummy = Stub::make('DummyClass', array('helloWorld' => Stub::consecutive('david', 'emma', 'sam', 'amy')));
  321. $this->assertEquals('david', $dummy->helloWorld());
  322. $this->assertEquals('emma', $dummy->helloWorld());
  323. $this->assertEquals('sam', $dummy->helloWorld());
  324. $this->assertEquals('amy', $dummy->helloWorld());
  325. // Expected null value when no more values
  326. $this->assertNull($dummy->helloWorld());
  327. }
  328. public function testStubPrivateProperties()
  329. {
  330. $tester = Stub::construct(
  331. 'MyClassWithPrivateProperties',
  332. ['name' => 'gamma'],
  333. [
  334. 'randomName' => 'chicken',
  335. 't' => 'ticky2',
  336. 'getRandomName' => function () {
  337. return "randomstuff";
  338. }
  339. ]
  340. );
  341. $this->assertEquals('gamma', $tester->getName());
  342. $this->assertEquals('randomstuff', $tester->getRandomName());
  343. $this->assertEquals('ticky2', $tester->getT());
  344. }
  345. public function testStubMakeEmptyInterface()
  346. {
  347. $stub = Stub::makeEmpty('\Countable', ['count' => 5]);
  348. $this->assertEquals(5, $stub->count());
  349. }
  350. }
  351. class MyClassWithPrivateProperties
  352. {
  353. private $name;
  354. private $randomName = "gaia";
  355. private $t = "ticky";
  356. public function __construct($name)
  357. {
  358. $this->name = $name;
  359. }
  360. public function getName()
  361. {
  362. return $this->name;
  363. }
  364. public function getRandomName()
  365. {
  366. return $this->randomName;
  367. }
  368. public function getT()
  369. {
  370. return $this->t;
  371. }
  372. }