Summer Sale! All accounts are 50% off this week.

bwrigley's avatar

Mocking traits?

Still very new to mocking and trying to figure out if I can (and indeed if I need to) mock traits?

I have a simple Post model that uses a trait called MediaTrait from Cloudinary that allows me to say $post->attachMedia($image)

I want to write a really simple unit test for the PostController::store() method that simply checks that the Post is correctly saved to the database with the correct fields.

All my assertions pass but I also get two files in cloudinary which I obviously don't want to do every time I test :(

Here is my Store method:


class PostController extends Controller
{

    protected $postableType = PostType::GENERIC;


    public function store(PostStoreRequest $request): RedirectResponse
    {

        $validated = $request->validated();

        $post = new Post();

        $post->message = $validated['message'];
        $post->user_id = $request->user()->id;

        $post->postable_type = $this->postableType;

        $post->save();

        if ($validated['images']){
            $post->addMedia($validated['images']);
        }

        $post->refreshCommentCountCache();

        return Redirect::route('feed');


    }

//

}

My Post model:

class Post extends Model
{
    use HasFactory, SoftDeletes;
    use MediaTrait;

    //

    public function addMedia(Array $images)
    {
        foreach($images as $file){
            $this->attachMedia($file);
        }
    }
}

My test:

class PostStoreMediaTest extends TestCase
{
    use RefreshDatabase;

    protected User $user;

    public function testStoreMethodAddsMediaToPost()
    {

        //ARRANGE

        // Mocking the dependencies and necessary objects
        $this->user = User::factory()->create();

        $request = $this->partialMock(PostStoreRequest::class, function(MockInterface $mock) {
            $mock->shouldReceive('user')->andReturn($this->user);
            $mock->shouldReceive('authorized')->andReturn(true);
            $mock->shouldReceive('validated')->andReturn([
                'message' => 'This is a test post message',
                'images' => [ UploadedFile::fake()->image('image1.jpg'),
                            UploadedFile::fake()->image('image2.jpg')],
            ]);
        });
        $media = $this->partialMock(MediaTrait::class, function (MockInterface $mock) {
            $mock->shouldReceive('attachMedia')->andReturn(null);
        });


        //ACT
        $controller = new PostController();
        $response = $controller->store($request);


        // ASSERT
        $this->assertDatabaseHas('posts', ['message' => 'This is a test post message']);
        $this->assertInstanceOf(RedirectResponse::class, $response);
        $this->assertEquals(route('feed'), $response->getTargetUrl());
    }
}

0 likes
2 replies
kevinbui's avatar
kevinbui
Best Answer
Level 41

Which PHP package are you using to interact with Cloudinary? I am looking at this MediaAlly trait, which is kinda similar to the that MediaTrait trait in your question.

If that is the case. you might want to mock that CloudinaryEngine class with the uploadFile behavior.

1 like
bwrigley's avatar

@kevinbui you are a gent! And urgh why didn't I think of that! Thanks again.

        $this->partialMock(CloudinaryEngine ::class, function(MockInterface $mock){
                 $mock->shouldReceive('uploadFile')->times(2)->andReturn($mock);
                 $mock->shouldReceive('getFileName')->once()->andReturn('Test File');
                 $mock->shouldReceive('getFileName')->once()->andReturn('Test File2');
                 $mock->shouldReceive('getSecurePath')->andReturn('some url');
                 $mock->shouldReceive('getSize')->andReturn(50);
                 $mock->shouldReceive('getFileType')->andReturn('jpg');
                 $mock->shouldReceive('getWidth')->andReturn(50);
                 $mock->shouldReceive('getHeight')->andReturn(50);

        });
2 likes

Please or to participate in this conversation.