soyhuce/laravel-testing is a Laravel package for helpers for laravel tests.
It currently has 3 GitHub stars and 14.867 downloads on Packagist (latest version 2.14.0).
Install it with composer require soyhuce/laravel-testing.
Discover more Laravel packages by soyhuce
or browse all Laravel packages to compare alternatives.
Last updated
Extra tools for your laravel tests
You can install the package via composer:
composer require soyhuce/laravel-testing --dev
To use Laravel specific assertions, you will have to add \Soyhuce\Testing\Assertions\LaravelAssertions::class trait to your test class.
Matches if the model is equal to the given model.
/** @test */
public function myTest()
{
$user1 = User::factory()->createOne();
$user2 = User::find($user1->id);
$this->assertIsModel($user1, $user2);
}
Matches if the collections are equal.
$collection1 = new Collection(['1', '2', '3']);
$collection2 = new Collection(['1', '2', '3']);
$this->assertCollectionEquals($collection1, $collection2);
2 Collections are considered equal if they contain the same elements, indexed by the same keys and in the same order.
$this->assertCollectionEquals(new Collection([1, 2]), new Collection([1, 2, 3])); // fail
$this->assertCollectionEquals(new Collection([1, 2, 3]), new Collection([1, 2])); // fail
$this->assertCollectionEquals(new Collection([1, 2, 3]), new Collection([3, 1, 2])); // fail
$this->assertCollectionEquals(new Collection([1, 2, 3]), new Collection([3, 1, 2])); // fail
$this->assertCollectionEquals(new Collection([1, 2, 3]), new Collection([1, 2, "3"])); // fail
$this->assertCollectionEquals(new Collection(['a' => 1, 'b' => 2, 'c' => 3]), new Collection(['a' => 1, 'b' => 2])); // fail
$this->assertCollectionEquals(new Collection(['a' => 1, 'b' => 2, 'c' => 3]), new Collection(['a' => 1, 'b' => 2, 'c' => 4])); // fail
$this->assertCollectionEquals(new Collection(['a' => 1, 'b' => 2, 'c' => 3]), new Collection(['a' => 1, 'b' => 2, 'd' => 3])); // fail
$this->assertCollectionEquals(new Collection(['a' => 1, 'b' => 2, 'c' => 3]), new Collection(['a' => 1, 'c' => 3, 'b' => 2])); // fail
$this->assertCollectionEquals(new Collection(['a' => 1, 'b' => 2, 'c' => 3]), new Collection(['a' => 1, 'b' => 2, 3])); // fail
If the Collections contain Models, assertCollectionEquals will use Model comparison of assertIsModel.
$user1 = User::factory()->createOne();
$user2 = User::find($user1->id);
$this->assertCollectionEquals(collect([$user1]), collect([$user2])); // Success
You can give an array in the $expected parameter of assertCollectionEquals :
/** @test */
public function theUsersAreOrdered(): void
{
$user1 = User::factory()->createOne();
$user2 = User::factory()->createOne();
$this->assertCollectionEquals(
[$user1, $user2],
User::query()->orderByDesc('id')->get()
);
}
Matches if the collections are canonically equals.
$collection1 = new Collection(['1', '2', '3']);
$collection2 = new Collection(['3', '2', '1']);
$this->assertCollectionEqualsCanonicalizing($collection1, $collection2);
2 Collections are considered equal if they contain the same elements, indexed by the same keys.
$this->assertCollectionEqualsCanonicalizing(new Collection([1, 2]), new Collection([1, 2, 3])); // fail
$this->assertCollectionEqualsCanonicalizing(new Collection([1, 2, 3]), new Collection([1, 2])); // fail
$this->assertCollectionEqualsCanonicalizing(new Collection([1, 2, 3]), new Collection([1, 2, "3"])); // fail
$this->assertCollectionEqualsCanonicalizing(new Collection(['a' => 1, 'b' => 2, 'c' => 3]), new Collection(['a' => 1, 'b' => 2])); // fail
$this->assertCollectionEqualsCanonicalizing(new Collection(['a' => 1, 'b' => 2, 'c' => 3]), new Collection(['a' => 1, 'b' => 2, 'c' => 4])); // fail
$this->assertCollectionEqualsCanonicalizing(new Collection(['a' => 1, 'b' => 2, 'c' => 3]), new Collection(['a' => 1, 'b' => 2, 3])); // fail
If the Collections contain Models, assertCollectionEqualsCanonicalizing will use Model comparison of assertIsModel.
$user1 = User::factory()->createOne();
$user2 = User::find($user1->id);
$this->assertCollectionEqualsCanonicalizing(collect([$user1]), collect([$user2])); // Success
You can give an array in the $expected parameter of assertCollectionEqualsCanonicalizing :
/** @test */
public function theUsersAreOrdered(): void
{
$user1 = User::factory()->createOne();
$user2 = User::factory()->createOne();
$this->assertCollectionEqualsCanonicalizing(
[$user1, $user2],
User::query()->get()
);
}
All these methods are available in Illuminate\Testing\TestResponse:
Requires hotmeteor/spectator package
TestResponse::assertValidContract(int $status) : Verifies that the request and the response are valid according to the contract.TestResponse::assertData($expect) : Alias for assertJsonPath('data', $expect)TestResponse::assertDataPath(string $path, $expect) : Alias for assertJsonPath('data.'.$path, $expect)TestResponse::assertDataPaths(array $expectations) : Runs assertDataPath for each $path => $expect pair in the array.TestResponse::assertDataPathCanonicalizing(string $path, array $expect) : Alias for assertJsonPathCanonicalizing('data.'.$path, $expect)TestResponse::assertDataPathsCanonicalizing(array<array> $expectations) : Runs assertDataPathCanonicalizing for each $path => $expect pair in the array.TestResponse::assertDataMissing($item) : Alias for assertJsonMissingPath('data', $item)TestResponse::assertDataPathMissing(string $path, $item) : Alias for assertJsonMissingPath('data.'.$path, $item)TestResponse::assertJsonPathMissing(string $path, $item) : Verifies that the Json path does not contain $itemTestResponse::assertJsonMessage(string $message) : Alias for assertJsonPath('message', $message)TestResponse::assertSimplePaginated() : Verifies that the response is a simple paginated response.TestResponse::assertPaginated() : Verifies that the response is a paginated response.TestResponse::assertViewHasNull(string $key) : Verifies that the key is present in the view but is null.It's possible to test FormRequests in isolation thanks to the TestsFormRequests trait.
$testFormRequest = $this->createRequest(CreateUserRequest::class);
$testFormRequest have some methods to check authorization and validation of the request.
TestFormRequest::by(Authenticable $user, ?string $guard = null) : set authenticated user in the requestTestFormRequest::withParams(array $params) : set route parametersTestFormRequest::withParam(string $param, mixed $value) : set a route parameterTestFormRequest::validate(array $data): TestValidationResult : get Validation resultTestFormRequest::assertAuthorized() : assert that the request is authorizedTestFormRequest::assertUnauthorized() : assert that the request is unauthorizedTestValidationResult::assertPasses() : assert that the validation passesTestValidationResult::assertFails(array $errors = []) : assert that the validation failsTestValidationResult::assertValidated(array $expected) : assert that the attributes and values that passed validation are the expected onesFor exemple :
$this->createRequest(CreateUserRequest::class)
->validate([
'name' => 'John Doe',
'email' => '[email protected]',
])
->assertPasses();
$this->createRequest(CreateUserRequest::class)
->validate([
'name' => null,
'email' => 12,
])
//->assertFails() We can check that the validation fails without defining the fields nor error messages
->assertFails([
'name' => 'The name field is required.',
'email' => [
'The email must be a string.',
'The email must be a valid email address.',
]
]);
$this->createRequest(CreateUserRequest::class)
->by($admin)
->assertAuthorized();
$this->createRequest(CreateUserRequest::class)
->by($user)
->assertUnauthorized();
$this->createRequest(UpdateUserRequest::class)
->withArg('user', $user)
->validate([
'email' => '[email protected]'
])
->assertPasses();
It's possible to test the JsonResources in isolation thanks to the TestsJsonResources trait.
TestsJsonResources::createResponse(JsonResource $resource, ?Request $request = null) returns a Illuminate\Testing\TestResponse.
$this->createResponse(UserResource::make($user))
->assertData([
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
]);
Let's take this test
$user = User::factory()->createOne();
$this->mock(DeleteUser::class)
->shouldReceive('execute')
->withArgs(function(User $executed) use ($user) {
$this->assertIsModel($user, $executed);
return true;
})
->once();
// run some code wich will execute the mock
We can simplify this test by using a Matcher.
$this->mock(DeleteUser::class)
->shouldReceive('execute')
->withArgs(Matcher::isModel($user))
->once();
For Collections, we can use Matcher::collectionEquals() or Matcher::collectionEqualsCanonicalizing().
For more complex cases, we can use Matcher::make.
$user = User::factory()->createOne();
$roles = Role::factory(2)->create();
$this->mock(UpdateUser::class)
->shouldReceive('execute')
->withArgs(function(User $executed, string $email, Collection $executedRoles) use ($user, $roles) {
$this->assertIsModel($user, $executed);
$this->assertSame('[email protected]', $email);
$this->assertCollectionEquals($roles, $executedRoles);
return true;
})
->once();
// Refactored to
$this->mock(UpdateUser::class)
->shouldReceive('execute')
->withArgs(Matcher::make(
$user,
'[email protected]',
$roles
))
->once();
In some cases, we wish to check only a few arguments or call argument methods:
$this->mock(CreateUser::class)
->shouldReceive('execute')
->withArgs(function(UserDTO $data, Collection $executedRoles) use ($team, $roles) {
$this->assertSame('[email protected]', $data->email);
$this->assertSame('password', $data->password);
$this->assertIsModel($team, $data->team())
$this->assertCollectionEquals($roles, $executedRoles);
return true;
})
->once();
We can use Matcher::match to define our assertions on $data:
$this->mock(CreateUser::class)
->shouldReceive('execute')
->withArgs(Matcher::make(
Matcher::match('[email protected]', fn(UserDTO $data) => $data->email)
->match('password', fn(UserDTO $data) => $data->password)
->match($team, fn(UserDTO $data) => $data->team()),
$roles
))
->once();
In specific cases of object properties, we can use named parameters:
$this->mock(CreateUser::class)
->shouldReceive('execute')
->withArgs(Matcher::make(
Matcher::match(email: '[email protected]', password: 'password')->match($team, fn(UserDTO $data) => $data->team()),
$roles
))
->once();
We can also check object type:
$this->mock(CreateUser::class)
->shouldReceive('execute')
->withArgs(Matcher::make(
Matcher::of(UserDTO::class)->properties(email: '[email protected]', password: 'password'),
$roles
))
->once();
The trait MocksActions provides a mockAction method to simply mock an action. By convention, an action is a class with a execute method.
Under the hood, it uses Mockery::mock.
It allows to easily define your action's expectations. Instead of
$user = User::factory()->createOne();
$this->mock(DeleteUser::class)
->shouldReceive('execute')
->withArgs(function(User $executed) use ($user) {
$this->assertIsModel($user, $executed);
return true;
})
->once();
you can write
$user = User::factory()->createOne();
$this->mockAction(DeleteUser::class)
->with($user);
You can also define the return value and capture it to use it in your test.
$this->mockAction(CreateUser::class)
->with(new UserData(email: '[email protected]', password: 'password'))
->returns(fn() => User::factory()->createOne())
->in($user);
$this->postJson('register', ['email' => '[email protected]', 'password' => 'password'])
->assertCreated()
->assertJson([
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
]);
It can be necessary to capture the return value of a callback, for exemple in returnUsing of mocks.
$this->mock(CreateOrUpdateVersion::class)
->expects('execute')
->andReturnUsing(
fn () => Version::factory()->for($package)->createOne()
)
->once();
// I need created Version ! How do I do ?
In this case, we will use capture function:
$this->mock(CreateOrUpdateVersion::class)
->expects('execute')
->andReturnUsing(capture(
$version,
fn () => Version::factory()->for($package)->createOne()
))
->once();
Once the mock executed, $version is created and will contain the returned value of the callback.
composer test
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.