How can we mock Stripe in Laravel Unit Tests without using any external package like stripe-mock etc?
The job is to test the Controller feature where the secret is hardcoded and due to which test is failing.
Hey i ran into the same problem,
i am using aspectmock from codeception. Gave me some grief setting it up but im now able to mock all the responses with a json response. this way the json data goes thru the stripe classes and it throws the correct errors and returns the same objects.
hope that helps
https://github.com/Codeception/AspectMock
public function testAll()
{
$customerClass = new StripeCustomers();
test::double('Stripe\HttpClient\CurlClient', ['request' => [json_encode($this->allJsonData), 200, []]]);
$customer = $customerClass->all();
$this->assertArrayHasKey('data', $customer);
}
Related
The bounty expires in 5 days. Answers to this question are eligible for a +50 reputation bounty.
Chris Rockwell wants to draw more attention to this question.
I am trying to get comfortable with tests in Laravel and playing around with Dusk.
Given I have the following controller:
class CoursesController extends Controller {
private ApiServiceProvider $api;
public function __construct(ApiServiceProvider $apiServiceProvider) {
$this->api = $apiServiceProvider;
}
public function getCoursesCache(array $cIds = []) : array {
// Breakpoint here - always gets hit when running tests
if (empty($cIds)) {
$cIds = Request::capture()->query('cIds');
$cIds = explode(',', $cIds);
}
return $this->api->getCoursesCache($cIds);
}
}
Which is used by a route:
Route::get('/api/v1/courses/cache', 'App\Http\Controllers\Api\CoursesController#getCoursesCache')->name('courses.cache');
This route is used internally by a VueJS component, which is ultimately what I'd like to test.
I am using Dusk to do some browser based testing and I want to mock the controller response for getCoursesCache. However, when I use the following (with a breakpoint in the controller method) I always enter the controller instead of just returning the mock.
$courseController = $this->mock(CoursesController::class)->makePartial();
$item = new CourseCacheItem();
$item->name = $course->name;
$courseController->shouldReceive('getCoursesCache')
->with([$course->getKey()])
->andReturn([$item]);
$this->browse(function (Browser $browser) use ($course) {
$browser->visit('/')
->waitFor('.course-card-container--data-loaded', 10)
->screenshot('filename')
->assertSee($course->name);
});
I've also tried this to create the mock:
$cc = $this->createMock(CoursesController::class);
$item = new CourseCacheItem();
$item->name = $course->name;
$cc->expects($this->once())->method('getCoursesCache')->with([$course->getKey()])->willReturn([$item]);
Edit: I've also now tried Mocking and Spying on the injected service ApiServiceProvider but the code enters that real class during the test run as well.
My expectation is that my breakpoint within the actual CoursesController would never be hit - what am I doing wrong?
Your issue here is that you are dealing with two separate Laravel Runtime.
You can check this issue for more infos.
Basically, when you are making a Dusk test with an Http call, Dusk will make a real Http call to a fresh Laravel instance/runtime (using Chrome Headless).
So you end up having one runtime where you are lunching the test, and the second one where dusk is making an http call.
The second Laravel instance is not running your Mock and doesnt know about it. That's why you end up in the actual Controller.
One solution i found in the past is making a route responsible to mock what i need.
This route should be called in the the html page before the assertion is happening.
So when you make a Dusk http call some Js (in the second Laravel instance) will call the route and the mock will be setup.
Fortunately for you there is great package that can handle this for you. https://github.com/NoelDeMartin/laravel-dusk-mocking
I have written two tests: one that makes a post request to an endpoint and awaits for a specific response containing status and message; and another one making the exact same request, but instead of await a response, it verifies if the database has the data matching what I just sent. Both these test are feature tests, and so far I have no unit test in my application; that happens because I have tested endpoint only.
So my idea is the following: instead of making a call to an endpoint in my second test, I could directly test my service method that creates a new register to the database. Would this be a valid unit test?
Personally, I think it would be valid because I am isolating a specific method and testing if the code works, and not if the integration works, even though there's integration of my code with the DB (Eloquent), my service method is the closest testable thing to the DB I have in my system.
My two tests, in the order I specified above:
/** #test */
public function a_group_can_be_created()
{
$this->withoutExceptionHandling()->signIn();
$group_data = [
'name' => $this->faker->word(),
'status' => $this->faker->boolean(),
];
$modules = ['modules' => Modules::factory(1)->create()->pluck('id')];
$response = $this->post(route('cms.groups.store'), array_merge($group_data, $modules));
$response->assertSessionHas('response', cms_response(trans('cms.groups.success_create')));
}
/** #test */
public function creating_a_group_persists_its_data_to_the_database()
{
$this->withoutExceptionHandling()->signIn();
$group_data = [
'name' => $this->faker->word(),
'status' => $this->faker->boolean(),
];
$modules = ['modules' => Modules::factory(1)->create()->pluck('id')];
$this->post(route('cms.groups.store'), array_merge($group_data, $modules));
$this->assertDatabaseHas('groups', $group_data);
$this->assertDatabaseCount('modules', 2);
$this->assertDatabaseCount('group_modules', 2);
}
Unit test in laravel "do not boot your Laravel application and therefore are unable to access your application's database or other framework services"
With that said, you can't access any database with a facade or with your eloquent model
so if you change your feature testing to unit testing, it will fail.
Unit test will work well if you don't use any of laravel framework utilities. I occasionally use it for testing a little self made library
But if you want to isolate it and create feature testing without calling the API endpoint, it will works too. It's really up to you to decide whether it is necessary to do that or not. But keep in mind that unnecessary test will make the test longer, especially if you use RefreshDatabase or DatabaseMigration trait. It will quite annoying to wait for them to finish
I have Postman API requests (POST request) containing grant_type = 'password', username = 'abc', password='xyz'. By passing this I get an token = 'xxx' and token_type = 'Bearer'. How do I write an phpunit test in laravel for such api requests?
you can make a test by php artisan make test:PassportTest.
Then you can use
$response = $this->json('POST','/api/token',['grant_type'=>'password','username'='abc','password'=>'xyz']);
$response-> assertJsonStructure([
'token'=>'xxx',
'token_type'=>'Bearer'
]);
You can also use $response->assertDatabaseHas($tokenTable,$data) to check if there is a record in you databse;
Cant see your database structures,so you need a little change to suit your own project.
I'm try to create a fake data for testing, and have the following code on the Behat Context
$modelFake = factory(User::class)->create();
$client = new GuzzleHttp\Client();
$res = $client->request('GET', url($this->apiURL . '/user/' . $modelFake->id));
After the class declaration I have:
use DatabaseTransactions;
The user fake it's created and I get the current Id to do the request, but the code return an empty object, If I remove the use DatabaseTransactions; the user persist on the database and the request return the object, but I can't realize what is my issue.
I hope someone can help me with this.
Regards
Edit:
When I try to get the user User::find($modelFake->id) I'm getting the object :(
Why when I made the request I can't get the object?
Edit 2:
The main problem it's the data persist only on the context and the request belong to another instance of the DB
I have been trying to test my controllers as per this tutorial -> Zend Framework 2.1 Unit testing
I have tried every possible variation of the code to send POST or GET data along with the dispatch but the only thing I get back from running the test is "Undefined Index" when I try to access that data from the $_POST array in the controller.
I am using PHPUnit 3.7.17, Everything else works perfectly except for POST and GET data, I have tried the following code:
public function testIndexActionCanBeAccessed() {
$this->getRequest()
->setMethod("POST")
->setPost(new \Zend\Stdlib\Parameters(array('argument' => 'value')));
$response = $this->dispatch('/app/api/index');
$this->assertResponseStatusCode(200);
}
AND
public function testIndexActionCanBeAccessed() {
$post_data = array("argument" => "value");
$response = $this->dispatch("/app/api/index", "POST", $post_data);
$this->assertResponseStatusCode(200);
}
I can't find any help on the web how to fix this issue. Can anyone help out? Any ideas?
$p = new Parameters();
$p->set('username','foo');
$p->set('password','bar');
$this->getRequest()->setMethod('POST');
$this->getRequest()->setPost($p);
$this->dispatch('/login');
This works for me. The Parameters() constructor doesn't seem to be what you're expecting it to be.
The docs say it takes an array, but I could only get it to work this way. The Stdlib\Parameters() constructor doesn't seem to do anything with the array passed in.
Get/Post data not sending, this issue is related to server, can you check the function use for fetching data from server.