What is the proper way to inject the cache backend?

Created on 20 April 2024, 9 months ago

Problem/Motivation

I am trying to test a custom service called get colors. It injects two services itself, one the database and the cache.data
I was trying to follow this example: https://git.drupalcode.org/project/opentelemetry/-/blob/1.0.x/tests/src/...

TestHelpers::service('database', initService: TRUE);
TestHelpers::service('cache.data', initService: TRUE);
$colorService = TestHelpers::service('custom_colors.get_colors', initService: TRUE);

Is this the intended pattern, or am I interpreting the tests incorrectly.

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

πŸ’¬ Support request
Status

Active

Version

1.3

Component

Documentation

Created by

πŸ‡ΊπŸ‡ΈUnited States nicxvan

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @nicxvan
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    I think this is missing the stub, I am not sure what the process is so I did a quick stub that just implements cachebackendinterface.

    If you can provide some insight on whether this is the right direction I can work on it more, but I'd like some confirmation this is the right direction.

  • Status changed to Needs review 8 months ago
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Please let me know if this is the right direction.

  • Pipeline finished with Success
    8 months ago
    Total: 171s
    #172956
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan
  • Status changed to Postponed: needs info 7 months ago
  • πŸ‡¦πŸ‡²Armenia murz Yerevan, Armenia

    If you don't need a custom cache, you can simply use the default stubs for that services, something like this:

    TestHelpers::service('database'); // Initiates the default stub of the database service.
    TestHelpers::service('cache.data'); // Initiates the default stub of the 'cache.data' service.
    $colorService = TestHelpers::service('custom_colors.get_colors', GetColorsService::class); // Initiates your service with dependencies.
    $result = $colorService->getColors();
    

    If you can provide a code of your 'custom_colors.get_colors', I can give more details about how to cover it.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    This along with πŸ› ConnectionStub throws error when fetchAll is called on execute. Fixed mostly solved my issue.

    In this service I'm collating data from some fields on a taxonomy term and flattening them in an array I can cache and access.

    In the test I'm creating the vocabulary, then a test term like this:

        $vocabulary = TestHelpers::saveEntity('taxonomy_vocabulary', [
          'vid' => 'part_color',
          'name' => 'Part Color',
        ]);
    
        $term = TestHelpers::saveEntity('taxonomy_term', [
          'vid' => $vocabulary->id(),
          'name' => 'A1',
          'parent' => 0,
          'field_color_code' => '23',
          'field_thirdparty_id' => 'thirdpartyId',
          'field_transparent' => TRUE,
        ]);
    
        TestHelpers::service('database');
        TestHelpers::service('cache.data');
        $customColors = TestHelpers::service('custom_colors.get_colors', customColors::class); 
        $colors = $customColors->getColors();
        print_r($colors);
        $color = reset($colors);
        $this->assertCount(1, $color['cid']);
    

    However in the code I'm doing a direct db call to join the fields since it's a lot more efficient in this case.

    If I understand it correctly I need to create the three additional fields expected field_color_code, field_thirdparty_id and field_transparent. Is that correct? Is there a test helper for creating mock fields like that?

  • πŸ‡¦πŸ‡²Armenia murz Yerevan, Armenia

    However in the code I'm doing a direct db call to join the fields since it's a lot more efficient in this case.

    If I understand it correctly I need to create the three additional fields expected field_color_code, field_thirdparty_id and field_transparent. Is that correct? Is there a test helper for creating mock fields like that?

    If you have a direct db call, the Unit test context can't emulate it, because we have no database instance available.
    So the only method to cover the SQL query in a unit test, is to check its contents manually, and then - create the expected result, manually too.

    Here is an example:

      public function mySelectQuery() {
        $database = \Drupal::service('database');
        $query = $database->select('users_field_data', 'u')
          ->condition('u.uid', 0, '<>')
          ->fields('u', ['uid', 'name', 'status', 'created', 'access'])
          ->range(0, 50);
        $result = $query->execute();
        $uids = $result->fetchAll();
        return $uids;
      }
    
      public function testMySelectQuery() {
        $expectedResult = ['1', '2', '3'];
        $database = TestHelpers::service('database');
        $database->stubSetExecuteHandler(function () use ($expectedResult) {
          // Checking if the query has all expected conditions.
          $queryExpected = TestHelpers::service('database')
            ->select('users_field_data', 'u')
            ->condition('u.uid', 0, '<>')
            ->range(0, 50);
          TestHelpers::queryIsSubsetOf($queryExpected, $this);
          
          // Creating a mock of the expected result for this query.
          $resultMock = TestHelpers::createMock(StatementWrapperIterator::class);
          $resultMock->method('fetchAll')->willReturn($expectedResult);
          return $resultMock;
        });
    
        $result = $this->testMySelectQuery();
        $this->assertEquals($expectedResult, $result);
      }
    

    So, this way of testing should work well on Test Helpers 1.3.0 even without the fix from the current issue.

  • πŸ‡¦πŸ‡²Armenia murz Yerevan, Armenia

    Also, please look into the MR from ✨ Add support for getting Database Stub using Database::getConnection() Needs review - it implements support for code, that uses the static class to get the database connection like this:

    $database = Database::getConnection();
    
  • πŸ‡¦πŸ‡²Armenia murz Yerevan, Armenia

    In the last release, I reworked the initialization of services with factories ( https://www.drupal.org/project/test_helpers/issues/3416420 ✨ Implement full initialization of services with a factory Fixed ) so the behavior of the module should be more predictable now, I hope.

  • Status changed to Closed: works as designed 5 months ago
  • πŸ‡¦πŸ‡²Armenia murz Yerevan, Armenia
Production build 0.71.5 2024