uuid: - value: aa4b919c-76e4-43db-8962-2b2cacd33267 langcode: - value: en type: - target_id: daily_email target_type: node_type target_uuid: 8bde1f2f-eef9-4f2d-ae9c-96921f8193d7 revision_timestamp: - value: '2025-05-11T09:00:20+00:00' revision_uid: - target_type: user target_uuid: b8966985-d4b2-42a7-a319-2e94ccfbb849 revision_log: { } status: - value: true uid: - target_type: user target_uuid: b8966985-d4b2-42a7-a319-2e94ccfbb849 title: - value: 'Experimenting with Architectural Testing' created: - value: '2024-02-08T00:00:00+00:00' changed: - value: '2025-05-11T09:00:20+00:00' promote: - value: false sticky: - value: false default_langcode: - value: true revision_translation_affected: - value: true path: - alias: /daily/2024/02/08/experimenting-with-architectural-testing langcode: en body: - value: |

In yesterday's email, I mentioned parallel testing and speeding up your tests by running them in parallel.

Something else I've been experimenting with recently in architectural testing with PHPat.

For example, ensuring classes within a namespace are final or not, that Controller classes all extend ControllerBase and have the Controller suffix in their names.

Going forward, I'd like to ensure that each Drupal module only uses its own classes and is separated, as I recently had an issue where I deleted a class method in one module only to find it was used in a different module.

Here's what I have so far for my testing course codebase:

final class ArchitectureTest {

        public function test_classes_should_be_final(): Rule {
        return PHPat::rule()
          ->classes(Selector::inNamespace('Drupal\atdc'))
          ->shouldBeFinal();
        }

        public function test_controllers_should_extend_ControllerBase(): Rule {
        return PHPat::rule()
          ->classes(Selector::inNamespace('Drupal\atdc\Controller'))
          ->shouldExtend()
          ->classes(Selector::classname(ControllerBase::class));
        }

        public function test_controllers_should_have_the_Controller_suffix(): Rule {
        return PHPat::rule()
          ->classes(Selector::inNamespace('Drupal\atdc\Controller'))
          ->shouldBeNamed(
            classname: '/Controller$/',
            regex: TRUE,
          );
        }

      }
      

I plan to continue expanding this configuration as I become more familiar with PHPat, and because it's a PHPStan extension, it's already available to run within my projects locally and within the CI pipeline.

format: full_html processed: |

In yesterday's email, I mentioned parallel testing and speeding up your tests by running them in parallel.

Something else I've been experimenting with recently in architectural testing with PHPat.

For example, ensuring classes within a namespace are final or not, that Controller classes all extend ControllerBase and have the Controller suffix in their names.

Going forward, I'd like to ensure that each Drupal module only uses its own classes and is separated, as I recently had an issue where I deleted a class method in one module only to find it was used in a different module.

Here's what I have so far for my testing course codebase:

final class ArchitectureTest {

        public function test_classes_should_be_final(): Rule {
        return PHPat::rule()
          ->classes(Selector::inNamespace('Drupal\atdc'))
          ->shouldBeFinal();
        }

        public function test_controllers_should_extend_ControllerBase(): Rule {
        return PHPat::rule()
          ->classes(Selector::inNamespace('Drupal\atdc\Controller'))
          ->shouldExtend()
          ->classes(Selector::classname(ControllerBase::class));
        }

        public function test_controllers_should_have_the_Controller_suffix(): Rule {
        return PHPat::rule()
          ->classes(Selector::inNamespace('Drupal\atdc\Controller'))
          ->shouldBeNamed(
            classname: '/Controller$/',
            regex: TRUE,
          );
        }

      }
      

I plan to continue expanding this configuration as I become more familiar with PHPat, and because it's a PHPStan extension, it's already available to run within my projects locally and within the CI pipeline.

summary: null field_daily_email_cta: { }