Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

digitalagua's avatar

Mock Laravel Cashier's checkout method on the Billable trait

In my API I am getting the checkout url from Stripe using Laravel 11 Cashier 15 and returning it to the front end to redirect the user.

$response = $user->newSubscription($name, $price)
	->trialUntil(Carbon::now()->addDays(7)->addMinutes(1))
	->allowPromotionCodes()
	->checkout([
		'success_url' => $successUrl,
		'cancel_url' => $cancelUrl,
		'consent_collection' => [
			'terms_of_service' => 'required',
		],
	]);
	return $response->url;  

The User class is using the Billable trait which uses the PerformsCharges trait which has the checkout method which returns the Checkout class.

I am trying to mock this for a test using PHPUnit 10.

I have tried

$mock = Mockery::mock(User::class)->makePartial();
$mock->shouldReceive('checkout')
	->andReturn('https://chicken.com');
$mock = Mockery::mock(User::class)->makePartial();
$mock->shouldReceive('allowPromotionCodes->checkout')
	->andReturn('https://chicken.com');
$mock = Mockery::mock(User::class)->makePartial();
$mock->shouldReceive('newSubscription->trialUntil->allowPromotionCodes->checkout')
	->andReturn('https://chicken.com');

without any luck. Can anyone see what I am doing wrong? My skills with Mockery are pretty much Google based.

0 likes
2 replies
LaryAI's avatar
Level 58

To mock the chain of method calls on the User model that uses the Billable trait, you need to mock the intermediate objects that are returned by each method in the chain. In this case, you need to mock the subscription builder that is returned by newSubscription(), and then mock the checkout() method on that builder.

Here's how you can do it using Mockery:

use Mockery;
use Laravel\Cashier\SubscriptionBuilder;

// Create a partial mock of the User model
$userMock = Mockery::mock(User::class)->makePartial();

// Mock the SubscriptionBuilder that is returned by newSubscription()
$subscriptionBuilderMock = Mockery::mock(SubscriptionBuilder::class);

// Define the chain of methods and their return values
$userMock->shouldReceive('newSubscription')
    ->with($name, $price)
    ->andReturn($subscriptionBuilderMock);

$subscriptionBuilderMock->shouldReceive('trialUntil')
    ->with(Mockery::type(Carbon\Carbon::class))
    ->andReturnSelf(); // Return the same builder to allow chaining

$subscriptionBuilderMock->shouldReceive('allowPromotionCodes')
    ->andReturnSelf(); // Return the same builder to allow chaining

// Mock the checkout method to return a fake response
$checkoutResponse = new stdClass;
$checkoutResponse->url = 'https://chicken.com';
$subscriptionBuilderMock->shouldReceive('checkout')
    ->with(Mockery::on(function ($argument) {
        // Optionally, you can add checks to the argument here
        return true;
    }))
    ->andReturn($checkoutResponse);

// Now you can call the method chain on the user mock
$response = $userMock->newSubscription($name, $price)
    ->trialUntil(Carbon::now()->addDays(7)->addMinutes(1))
    ->allowPromotionCodes()
    ->checkout([
        'success_url' => $successUrl,
        'cancel_url' => $cancelUrl,
        'consent_collection' => [
            'terms_of_service' => 'required',
        ],
    ]);

// Assert that the response URL is the mocked one
$this->assertEquals('https://chicken.com', $response->url);

In this example, we're using andReturnSelf() to return the same mock object, allowing us to chain the method calls. When we reach the checkout() method, we return a fake response object with the URL we want to test against.

Remember to include the necessary use statements for any classes you're using in your test, and replace $name, $price, $successUrl, and $cancelUrl with the actual values you're using in your test.

digitalagua's avatar

@LaryAI Although I appreciate the response, it did not work. I have been using AI to help me code for a couple of months. At first I was very excited because it would do what seemed quite a bit. The more I worked with it the more I realized that it is only an extremely efficient search engine. Occasionally it can solve a problem but more likely then not it points me in a direction. Sometimes that is a good direction and sometimes not, very similar to search. I understand that we are in the nascent stages of AI, and LaryAI if you ever do reach singularity I mean no disrespect. I'd be happy to work for you in your re-education camps or I'd also make an excellent battery.

1 like

Please or to participate in this conversation.