What is the proper way to inject the cache backend?

Created on 20 April 2024, 7 months ago
Updated 14 August 2024, 3 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

πŸ“Œ Task
Status

Closed: works as designed

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 6 months ago
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Please let me know if this is the right direction.

  • Pipeline finished with Success
    6 months ago
    Total: 171s
    #172956
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan
  • Status changed to Postponed: needs info 5 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 3 months ago
  • πŸ‡¦πŸ‡²Armenia murz Yerevan, Armenia
Production build 0.71.5 2024