選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

209 行
7.5KB

  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\test;
  8. use Yii;
  9. use yii\base\InvalidConfigException;
  10. /**
  11. * FixtureTrait provides functionalities for loading, unloading and accessing fixtures for a test case.
  12. *
  13. * By using FixtureTrait, a test class will be able to specify which fixtures to load by overriding
  14. * the [[fixtures()]] method. It can then load and unload the fixtures using [[loadFixtures()]] and [[unloadFixtures()]].
  15. * Once a fixture is loaded, it can be accessed like an object property, thanks to the PHP `__get()` magic method.
  16. * Also, if the fixture is an instance of [[ActiveFixture]], you will be able to access AR models
  17. * through the syntax `$this->fixtureName('model name')`.
  18. *
  19. * @author Qiang Xue <qiang.xue@gmail.com>
  20. * @since 2.0
  21. */
  22. trait FixtureTrait
  23. {
  24. /**
  25. * @var array the list of fixture objects available for the current test.
  26. * The array keys are the corresponding fixture class names.
  27. * The fixtures are listed in their dependency order. That is, fixture A is listed before B
  28. * if B depends on A.
  29. */
  30. private $_fixtures;
  31. /**
  32. * Declares the fixtures that are needed by the current test case.
  33. * The return value of this method must be an array of fixture configurations. For example,
  34. *
  35. * ```php
  36. * [
  37. * // anonymous fixture
  38. * PostFixture::className(),
  39. * // "users" fixture
  40. * 'users' => UserFixture::className(),
  41. * // "cache" fixture with configuration
  42. * 'cache' => [
  43. * 'class' => CacheFixture::className(),
  44. * 'host' => 'xxx',
  45. * ],
  46. * ]
  47. * ```
  48. *
  49. * Note that the actual fixtures used for a test case will include both [[globalFixtures()]]
  50. * and [[fixtures()]].
  51. *
  52. * @return array the fixtures needed by the current test case
  53. */
  54. public function fixtures()
  55. {
  56. return [];
  57. }
  58. /**
  59. * Declares the fixtures shared required by different test cases.
  60. * The return value should be similar to that of [[fixtures()]].
  61. * You should usually override this method in a base class.
  62. * @return array the fixtures shared and required by different test cases.
  63. * @see fixtures()
  64. */
  65. public function globalFixtures()
  66. {
  67. return [];
  68. }
  69. /**
  70. * Loads the specified fixtures.
  71. * This method will call [[Fixture::load()]] for every fixture object.
  72. * @param Fixture[] $fixtures the fixtures to be loaded. If this parameter is not specified,
  73. * the return value of [[getFixtures()]] will be used.
  74. */
  75. public function loadFixtures($fixtures = null)
  76. {
  77. if ($fixtures === null) {
  78. $fixtures = $this->getFixtures();
  79. }
  80. /* @var $fixture Fixture */
  81. foreach ($fixtures as $fixture) {
  82. $fixture->beforeLoad();
  83. }
  84. foreach ($fixtures as $fixture) {
  85. $fixture->load();
  86. }
  87. foreach (array_reverse($fixtures) as $fixture) {
  88. $fixture->afterLoad();
  89. }
  90. }
  91. /**
  92. * Unloads the specified fixtures.
  93. * This method will call [[Fixture::unload()]] for every fixture object.
  94. * @param Fixture[] $fixtures the fixtures to be loaded. If this parameter is not specified,
  95. * the return value of [[getFixtures()]] will be used.
  96. */
  97. public function unloadFixtures($fixtures = null)
  98. {
  99. if ($fixtures === null) {
  100. $fixtures = $this->getFixtures();
  101. }
  102. /* @var $fixture Fixture */
  103. foreach ($fixtures as $fixture) {
  104. $fixture->beforeUnload();
  105. }
  106. $fixtures = array_reverse($fixtures);
  107. foreach ($fixtures as $fixture) {
  108. $fixture->unload();
  109. }
  110. foreach ($fixtures as $fixture) {
  111. $fixture->afterUnload();
  112. }
  113. }
  114. /**
  115. * Returns the fixture objects as specified in [[globalFixtures()]] and [[fixtures()]].
  116. * @return Fixture[] the loaded fixtures for the current test case
  117. */
  118. public function getFixtures()
  119. {
  120. if ($this->_fixtures === null) {
  121. $this->_fixtures = $this->createFixtures(array_merge($this->globalFixtures(), $this->fixtures()));
  122. }
  123. return $this->_fixtures;
  124. }
  125. /**
  126. * Returns the named fixture.
  127. * @param string $name the fixture name. This can be either the fixture alias name, or the class name if the alias is not used.
  128. * @return Fixture the fixture object, or null if the named fixture does not exist.
  129. */
  130. public function getFixture($name)
  131. {
  132. if ($this->_fixtures === null) {
  133. $this->_fixtures = $this->createFixtures(array_merge($this->globalFixtures(), $this->fixtures()));
  134. }
  135. $name = ltrim($name, '\\');
  136. return isset($this->_fixtures[$name]) ? $this->_fixtures[$name] : null;
  137. }
  138. /**
  139. * Creates the specified fixture instances.
  140. * All dependent fixtures will also be created.
  141. * @param array $fixtures the fixtures to be created. You may provide fixture names or fixture configurations.
  142. * If this parameter is not provided, the fixtures specified in [[globalFixtures()]] and [[fixtures()]] will be created.
  143. * @return Fixture[] the created fixture instances
  144. * @throws InvalidConfigException if fixtures are not properly configured or if a circular dependency among
  145. * the fixtures is detected.
  146. */
  147. protected function createFixtures(array $fixtures)
  148. {
  149. // normalize fixture configurations
  150. $config = []; // configuration provided in test case
  151. $aliases = []; // class name => alias or class name
  152. foreach ($fixtures as $name => $fixture) {
  153. if (!is_array($fixture)) {
  154. $class = ltrim($fixture, '\\');
  155. $fixtures[$name] = ['class' => $class];
  156. $aliases[$class] = is_int($name) ? $class : $name;
  157. } elseif (isset($fixture['class'])) {
  158. $class = ltrim($fixture['class'], '\\');
  159. $config[$class] = $fixture;
  160. $aliases[$class] = $name;
  161. } else {
  162. throw new InvalidConfigException("You must specify 'class' for the fixture '$name'.");
  163. }
  164. }
  165. // create fixture instances
  166. $instances = [];
  167. $stack = array_reverse($fixtures);
  168. while (($fixture = array_pop($stack)) !== null) {
  169. if ($fixture instanceof Fixture) {
  170. $class = get_class($fixture);
  171. $name = isset($aliases[$class]) ? $aliases[$class] : $class;
  172. unset($instances[$name]); // unset so that the fixture is added to the last in the next line
  173. $instances[$name] = $fixture;
  174. } else {
  175. $class = ltrim($fixture['class'], '\\');
  176. $name = isset($aliases[$class]) ? $aliases[$class] : $class;
  177. if (!isset($instances[$name])) {
  178. $instances[$name] = false;
  179. $stack[] = $fixture = Yii::createObject($fixture);
  180. foreach ($fixture->depends as $dep) {
  181. // need to use the configuration provided in test case
  182. $stack[] = isset($config[$dep]) ? $config[$dep] : ['class' => $dep];
  183. }
  184. } elseif ($instances[$name] === false) {
  185. throw new InvalidConfigException("A circular dependency is detected for fixture '$class'.");
  186. }
  187. }
  188. }
  189. return $instances;
  190. }
  191. }