I need a small help with a simple test. I'm trying to test blog post delete endpoint where it deletes both the post and its comments. In the postController method, it calls two other methods from two other repositories (postRepository and commentsRepository) to delete the post completely. Here's how the method in the controller looks like:
public function deletePost(Request $request, $postId)
{
$this->postRepository()->deletePost($postId);
$this->commentsRepository()->deleteComments($postId);
return $this->response(new JsonResponse('', 204));
}
I only need to check if deletePost method and deleteComments are being called once. Right now I'm getting this:
Expectation failed for method name is "deletePost" when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
Same as deleteComments. Now I feel like the mock is not working properly. Here's postRepository's deletePost method and commentRepository's deleteComments method:
// postRepository's
public function deletePost($postId)
{
// ... some code
}
// commentsRepository
public function deleteComments($postId)
{
// ... some code
}
Here's the test:
public function testDelete()
{
$post = $this->postRepository()
->findOne(['id' => PostDataSeeder::POST_ID]);
$route = $this->getUrl('api_delete_post', [
'_format' => 'json',
'postId' => $post->getId()
]);
$this->client->request('DELETE', $route, [], [], [
'CONTENT_TYPE' => 'application/json',
]);
// check if deletePost method in postRepository is being called
$postRepositoryMock = $this->createMock(PostRepository::class);
$postRepositoryMock
->expects($this->once())
->method('deletePost')
->with($post->getId());
// check if deleteComments method in commentsRepository is being called
$commentsRepositoryMock = $this->createMock(CommentsRepository::class);
$commentsRepositoryMock
->expects($this->once())
->method('deleteComments')
->with($post->getId());
}
This is the minimal representation of my actual code. Everything works perfectly, except the test. Please help me how to test that.
CodePudding user response:
When you make the call via $this->client->request...
you invoke the unchanged controller and therefore unchanged (not mocked) repositories.
You should substitute repositories in your container before issuing the request and 'running' the it on mocked classes. This can be achieved by Symfonys' env-specific services file i.e. services_test.yml
or test/services.yml
The bigger problem I see here is that you're trying to run webTests using mocks, which is a bit of a mix of two approaches: web test and unit test.
Web tests should run on unmodified code (as much as it can be done) but on fixed environment. In this case that would mean a separate test database filled with predefined data (fixtures can do that). After the request is made you can get the repo from container and check if the entry was actually deleted.
Unit test is closer to what you are doing. You first create mocks of all dependencies of postController
and instantiate it in your test. Then run $postControllerInstance->deletePost(...)
with all arguments mocked (or just instantiated objects) and then check which mocked function has been called.
Summarizing in not entirely correct sentence: web tests should check if everything works correctly; Unit tests should check if everything is being called correctly.