Which class should i mock service class or third party api of service class?

Viewed 38

PostController's store method which calls the service class and service class calls the third party api i.e. line. while storing a post. i want to write testcase if the notify field is true then it sends notification to the user's through line if not then return with error message. i am not getting any idea how to perform this test. here is the code for PostController.php

private Post $post;
private PostService $service;

public function __construct(
    PostService $service,
    Post $post
) {
    $this->service = $service;
    $this->post = $post;
}

public function store(PostRequest $request): RedirectResponse
{
    $post = $this->service->createPost($request->validated());
    if ($request->notify) {

        $message = 'lorem ipsum';
        $this->service->lineSendToGroup($request->category_big_id, $message);
    }

    return redirect()->to('/posts')->with('success_message', 'Post created successfully.');
}

PostService.php

use App\Library\Line;
use App\Models\CategoryBig;

class PostService
{
private CategoryBig $categoryBig;

public function __construct(
    CategoryBig $categoryBig,
) {
    $this->categoryBig = $categoryBig;
}

public function lineSendToGroup(int $categoryBigId, string $message): void
{
    $catB = $this->findOrFailCategoryBig($categoryBigId);
    Line::send(
        $catB->line_message_channel_secret,
        $catB->line_message_channel_access_token,
        $catB->line_group_id,
        $message
    );
}

public function findOrFailCategoryBig(int $categoryBigId): CategoryBig
{
    return $this->categoryBig->whereId($categoryBigId)->firstOrFail();
}

public function createPost(array $createData): Post
{
    $createData += [$data];
    return $this->greeting->create($createData);
}

Line.php

namespace App\Library;

use Illuminate\Support\Carbon;
use LINE\LINEBot;
use LINE\LINEBot\HTTPClient\CurlHTTPClient;
use LINE\LINEBot\Event\MessageEvent\TextMessage;
use LINE\LINEBot\MessageBuilder\TextMessageBuilder;
use Log;

class Line
{
    public static function send($channel_secret, $access_token, $line_user_id, $message)
    {
        $http_client = new CurlHTTPClient($access_token);
        $bot         = new LINEBot($http_client, ['channelSecret' => $channel_secret]);

        $textMessageBuilder = new TextMessageBuilder($message);
        $response    = $bot->pushMessage($line_user_id, $textMessageBuilder);

        if ($response->isSucceeded()) {
            return true;
        } else {
            return false;
        }
    }
}

Test

    
public function test_notification_when_LINE_notification_is_specified()
{

}
1 Answers

Step 1)

You can write a unit test for Line class.

For ease of use, you can refactor this class and use Laravel Http client instead. Create different stubs for each line's response (e.g 200) then use Laravel testing helpers to mock resposne.

Step 2)

Write a feature test for your controller. In this test you don't care about internal functionality of Line class and just want to make sure method of this class is called with proper arguments. You can use mockery to achieve that.

Migrate to non-static method on Line class. You can't mock static methods.

For happy path test:

$mock = Mockery::mock(\App\Library\Line::class);
    $mock->shouldReceive('send')
        ->once()
        ->with($secret, $token, $groupId, $message)
        ->andReturn(true);

// of course mocking object doesn't do much alone and you need to bind and resolve it.
$this->app->instance(\App\Library\Line::class, $mock);

Then resolve it inside your PostService@lineSendToGroup

resolve(Line::class, [
    'token' => $token,
    // ... other args

])

For opposite scenario use shouldNotReceive('send') instead.

Related