class CronTest

Same name and namespace in other branches
  1. 9 core/tests/Drupal/Tests/Core/CronTest.php \Drupal\Tests\Core\CronTest
  2. 11.x core/tests/Drupal/Tests/Core/CronTest.php \Drupal\Tests\Core\CronTest

Tests the Cron class.

@group Cron @coversDefaultClass \Drupal\Core\Cron

Hierarchy

Expanded class hierarchy of CronTest

File

core/tests/Drupal/Tests/Core/CronTest.php, line 31

Namespace

Drupal\Tests\Core
View source
class CronTest extends UnitTestCase {
  const REQUEUE_COUNT = 3;
  
  /**
   * Define the duration of each item claim for this test.
   *
   * @var int
   */
  protected $claimTime = 300;
  
  /**
   * An instance of the Cron class for testing.
   *
   * @var \Drupal\Core\Cron
   */
  protected $cron;
  
  /**
   * The queue used to store test work items.
   *
   * @var \Drupal\Core\Queue\QueueInterface
   */
  protected $queue;
  
  /**
   * The current state of the test in memory.
   *
   * @var \Drupal\Core\State\State
   */
  protected $state;
  
  /**
   * {@inheritdoc}
   */
  protected function setUp() : void {
    parent::setUp();
    // Construct a state object used for testing logger assertions.
    $time = $this->prophesize(TimeInterface::class)
      ->reveal();
    $this->state = new State(new KeyValueMemoryFactory(), new MemoryBackend($time), new NullLockBackend());
    // Create a mock logger to set a flag in the resulting state.
    $logger = $this->prophesize('Drupal\\Core\\Logger\\LoggerChannelInterface');
    // Safely ignore the cron success message.
    $logger->info('Cron run completed.')
      ->shouldBeCalled();
    // Set a flag to track when a message is logged by adding a callback
    // function for each logging method.
    foreach (get_class_methods(LoggerInterface::class) as $logger_method) {
      $logger->{$logger_method}(Argument::cetera())
        ->will(function () {
        \Drupal::state()->set('cron_test.message_logged', TRUE);
      });
    }
    // Create a logger factory to produce the resulting logger.
    $logger_factory = $this->prophesize('Drupal\\Core\\Logger\\LoggerChannelFactoryInterface');
    $logger_factory->get(Argument::exact('cron'))
      ->willReturn($logger->reveal());
    // Create a mock time service.
    $time = $this->prophesize('Drupal\\Component\\Datetime\\TimeInterface');
    // Create a mock config factory and config object.
    $config_factory = $this->prophesize(ConfigFactoryInterface::class);
    $config = $this->prophesize(ImmutableConfig::class);
    $config->get('logging')
      ->willReturn(FALSE);
    $config_factory->get('system.cron')
      ->willReturn($config->reveal());
    // Build the container using the resulting mock objects.
    \Drupal::setContainer(new ContainerBuilder());
    \Drupal::getContainer()->set('logger.factory', $logger_factory->reveal());
    \Drupal::getContainer()->set('datetime.time', $time->reveal());
    \Drupal::getContainer()->set('state', $this->state);
    \Drupal::getContainer()->set('config.factory', $config_factory->reveal());
    // Create mock objects for constructing the Cron class.
    $module_handler = $this->prophesize('Drupal\\Core\\Extension\\ModuleHandlerInterface');
    $queue_factory = $this->prophesize('Drupal\\Core\\Queue\\QueueFactory');
    $queue_worker_manager = $this->prophesize('Drupal\\Core\\Queue\\QueueWorkerManagerInterface');
    $state = $this->prophesize('Drupal\\Core\\State\\StateInterface');
    $account_switcher = $this->prophesize('Drupal\\Core\\Session\\AccountSwitcherInterface');
    $queueConfig = [
      'suspendMaximumWait' => 30.0,
    ];
    // Create a lock that will always fail when attempting to acquire; we're
    // only interested in testing ::processQueues(), not the other stuff.
    $lock_backend = $this->prophesize('Drupal\\Core\\Lock\\LockBackendInterface');
    $lock_backend->acquire('cron', Argument::cetera())
      ->willReturn(TRUE);
    $lock_backend->release('cron')
      ->shouldBeCalled();
    // Create a queue worker definition for testing purposes.
    $queue_worker = $this->randomMachineName();
    $queue_worker_definition = [
      'id' => $queue_worker,
      'cron' => [
        'time' => &$this->claimTime,
      ],
    ];
    // Create a queue instance for this queue worker.
    $this->queue = new Memory($queue_worker);
    $queue_factory->get($queue_worker)
      ->willReturn($this->queue);
    // Create a mock queue worker plugin instance based on above definition.
    $queue_worker_plugin = $this->prophesize('Drupal\\Core\\Queue\\QueueWorkerInterface');
    $queue_worker_plugin->getPluginId()
      ->willReturn($queue_worker);
    $queue_worker_plugin->getPluginDefinition()
      ->willReturn($queue_worker_definition);
    $queue_worker_plugin->processItem('Complete')
      ->willReturn();
    $queue_worker_plugin->processItem('Exception')
      ->willThrow(\Exception::class);
    $queue_worker_plugin->processItem('DelayedRequeueException')
      ->willThrow(DelayedRequeueException::class);
    $queue_worker_plugin->processItem('SuspendQueueException')
      ->willThrow(SuspendQueueException::class);
    // 'RequeueException' would normally result in an infinite loop.
    //
    // This is avoided by throwing RequeueException for the first few calls to
    // ::processItem() and then returning void. ::testRequeueException()
    // establishes sanity assertions for this case.
    $queue_worker_plugin->processItem('RequeueException')
      ->will(function ($args, $mock, $method) {
      // Fetch the number of calls to this prophesied method. This value will
      // start at zero during the first call.
      $method_calls = count($mock->findProphecyMethodCalls($method->getMethodName(), new ArgumentsWildcard($args)));
      // Throw the expected exception on the first few calls.
      if ($method_calls < self::REQUEUE_COUNT) {
        \Drupal::state()->set('cron_test.requeue_count', $method_calls + 1);
        throw new RequeueException();
      }
    });
    // Set the mock queue worker manager to return the definition/plugin.
    $queue_worker_manager->getDefinitions()
      ->willReturn([
      $queue_worker => $queue_worker_definition,
    ]);
    $queue_worker_manager->createInstance($queue_worker)
      ->willReturn($queue_worker_plugin->reveal());
    // Construct the Cron class to test.
    $this->cron = new Cron($module_handler->reveal(), $lock_backend->reveal(), $queue_factory->reveal(), $state->reveal(), $account_switcher->reveal(), $logger->reveal(), $queue_worker_manager->reveal(), $time->reveal(), $queueConfig);
  }
  
  /**
   * Resets the testing state.
   */
  protected function resetTestingState() {
    $this->queue
      ->deleteQueue();
    $this->state
      ->set('cron_test.message_logged', FALSE);
    $this->state
      ->set('cron_test.requeue_count', NULL);
  }
  
  /**
   * Data provider for ::testProcessQueues() method.
   */
  public static function processQueuesTestData() {
    return [
      [
        'Complete',
        'assertFalse',
        0,
      ],
      [
        'Exception',
        'assertTrue',
        1,
      ],
      [
        'DelayedRequeueException',
        'assertFalse',
        1,
      ],
      [
        'SuspendQueueException',
        'assertTrue',
        1,
      ],
      [
        'RequeueException',
        'assertFalse',
        0,
      ],
    ];
  }
  
  /**
   * Tests the ::processQueues() method.
   *
   * @covers ::processQueues
   * @dataProvider processQueuesTestData
   */
  public function testProcessQueues($item, $message_logged_assertion, $count_post_run) : void {
    $this->resetTestingState();
    $this->queue
      ->createItem($item);
    $this->assertFalse($this->state
      ->get('cron_test.message_logged'));
    $this->assertEquals(1, $this->queue
      ->numberOfItems());
    $this->cron
      ->run();
    $this->{$message_logged_assertion}($this->state
      ->get('cron_test.message_logged'));
    $this->assertEquals($count_post_run, $this->queue
      ->numberOfItems());
  }
  
  /**
   * Verify that RequeueException causes an item to be processed multiple times.
   */
  public function testRequeueException() : void {
    $this->resetTestingState();
    $this->queue
      ->createItem('RequeueException');
    $this->cron
      ->run();
    // Fetch the number of times this item was requeued.
    $actual_requeue_count = $this->state
      ->get('cron_test.requeue_count');
    // Make sure the item was requeued at least once.
    $this->assertIsInt($actual_requeue_count);
    // Ensure that the actual requeue count matches the expected value.
    $this->assertEquals(self::REQUEUE_COUNT, $actual_requeue_count);
  }

}

Members

Title Sort descending Deprecated Modifiers Object type Summary Overriden Title Overrides
CronTest::$claimTime protected property Define the duration of each item claim for this test.
CronTest::$cron protected property An instance of the Cron class for testing.
CronTest::$queue protected property The queue used to store test work items.
CronTest::$state protected property The current state of the test in memory.
CronTest::processQueuesTestData public static function Data provider for ::testProcessQueues() method.
CronTest::REQUEUE_COUNT constant
CronTest::resetTestingState protected function Resets the testing state.
CronTest::setUp protected function Overrides UnitTestCase::setUp
CronTest::testProcessQueues public function Tests the ::processQueues() method.
CronTest::testRequeueException public function Verify that RequeueException causes an item to be processed multiple times.
PhpUnitWarnings::$deprecationWarnings private static property Deprecation warnings from PHPUnit to raise with @trigger_error().
PhpUnitWarnings::addWarning public function Converts PHPUnit deprecation warnings to E_USER_DEPRECATED.
RandomGeneratorTrait::getRandomGenerator protected function Gets the random generator for the utility methods.
RandomGeneratorTrait::randomMachineName protected function Generates a unique random string containing letters and numbers.
RandomGeneratorTrait::randomObject public function Generates a random PHP object.
RandomGeneratorTrait::randomString public function Generates a pseudo-random string of ASCII characters of codes 32 to 126.
RandomGeneratorTrait::randomStringValidate Deprecated public function Callback for random string validation.
UnitTestCase::$root protected property The app root. 1
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getConfigStorageStub public function Returns a stub config storage that returns the supplied configuration.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::setUpBeforeClass public static function
UnitTestCase::__get public function

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.