← Back to projects
Phpunit Architecture Test
Methods for testing application architecture
#PHPUnit Application Architecture Test
Idea: write architecture tests as well as feature and unit tests. Protect your architecture code style!
#Example
Don't use repositories in controllers use only in services classes. Take three layers "repositories", "services", "controllers" and add asserts on dependencies.
$controllers = $this->layer()->leaveByNameStart('App\\Controllers'); $services = $this->layer()->leaveByNameStart('App\\Services'); $repositories = $this->layer()->leaveByNameStart('App\\Repositories'); $this->assertDoesNotDependOn($controllers, $repositories); $this->assertDependOn($controllers, $services); $this->assertDependOn($services, $repositories);
#Installation
#Install via composer
composer require --dev ta-tikoma/phpunit-architecture-test
#Add trait to Test class
abstract class TestCase extends BaseTestCase { use ArchitectureAsserts; }
#Use
- Create test
- Make layers of application
- Add asserts
public function test_make_layer_from_namespace() { $app = $this->layer()->leaveByNameStart('PHPUnit\\Architecture'); $tests = $this->layer()->leaveByNameStart('tests'); $this->assertDoesNotDependOn($app, $tests); $this->assertDependOn($tests, $app); }
#Run
./vendor/bin/phpunit
#Test files structure
- tests
- Architecture
- SomeTest.php
- Feature
- Unit
- Architecture
#How to build Layer
$this->layer()take access to layer with all objects and filter for create your layer:- leave objects in layer only:
->leave($closure)by closure->leaveByPathStart($path)by object path start->leaveByNameStart($name)by object name start->leaveByNameRegex($name)by object name regex->leaveByType($name)by object type
- remove objects from layer:
->exclude($closure)by closure->excludeByPathStart($path)by object path start->excludeByNameStart($name)by object name start->excludeByNameRegex($name)by object name regex->excludeByType($name)by object type
- leave objects in layer only:
- you can create multiple layers with split:
->split($closure)by closure->splitByNameRegex($closure)by object name
#Asserts
#Dependencies
Example: Controllers don't use Repositories only via Services
assertDependOn($A, $B)Layer A must contains dependencies by layer B.assertDoesNotDependOn($A, $B)Layer A (or layers in array A) must not contains dependencies by layer B (or layers in array B).
#Methods
assertIncomingsFrom($A, $B)Layer A must contains arguments with types from Layer BassertIncomingsNotFrom($A, $B)Layer A must not contains arguments with types from Layer BassertOutgoingFrom($A, $B)Layer A must contains methods return types from Layer BassertOutgoingNotFrom($A, $B)Layer A must not contains methods return types from Layer BassertMethodSizeLessThan($A, $SIZE)Layer A must not contains methods with size less than SIZE
#Properties
assertHasNotPublicProperties($A)Objects in Layer A must not contains public properties
#Essence
You can use $layer->essence($path) method for collect data from layer. For example get visibility of all properties in layer: $visibilities = $layer->essence('properties.*.visibility'); .
assertEach($list, $check, $message)- each item of list must passed tested by $check-functionassertNotOne($list, $check, $message)- not one item of list must not passed tested by $check-functionassertAny($list, $check, $message)- one or more item of list must not passed tested by $check-function
#Alternatives
#Advantages
- Dynamic creation of layers by regular expression (not need declare each module)
- Run along with the rest of tests from phpunit
- Asserts to method arguments and return types (for check dependent injection)
- Asserts to properties visibility