i recently moved from laravel 5.2 to laravel 5.4. In 5.2 all testcases were working fine. After upgrading to new version i am facing issues with testcases. Below is my sample code that was working fine in 5.2.
$params = [
'id' => 'asddsdsd'
];
$response1 = $this->call('GET', '/user/getInfo', $params);
$this->assertArrayHasKey('status', $response1);
$this->assertTrue($response1['status'] == 400);
$params2 = [
'id' => '23'
];
$response2 = $this->call('GET', '/user/getInfo', $params2);
$this->assertArrayHasKey('status', $response2);
$this->assertTrue($response2['status'] == 200);
in laravel 5.2 both used to have seperate response, in first case i used to get Failure response and second case, i was getting Success response.
After upgrading to 5.4, i am gettign same response for two testcases. Basically, the first call response is copied to second response because of calling same URl. This is happening for all the testcases.
Note: I added second case in different method, but still facing the same issue
Can anyone suggest me how to fix this??
Looks like the call() method returns a Response object in 5.2, but a TestResponse object in 5.4. To make your code comparable in 5.4, you could try using the baseResponse property of your TestResponse object:
$this->assertArrayHasKey('status', $response1->baseResponse);
You might also need to separate your 2 requests into separate modular test cases and then link them using #depends annotation.
public function testRequest1()
{
$params = [
'id' => 'asddsdsd'
];
$response1 = $this->call('GET', '/user/getInfo', $params);
//$this->assertWhatever()
return $dataIfYouWantIt
}
/**
* #depends testRequest1
*/
public function testRequest2($dataIfYouWantIt)
{
$params2 = [
'id' => '23'
];
$response2 = $this->call('GET', '/user/getInfo', $params2);
//$this->assertWhatever()
}
Related
I would like to separate Tests and Data Providers. Using PHP 8 attributes, I cannot get the following test to run when referencing an external Data Provider:
#[Test]
#[DataProviderExternal(RouterDataProvider::class, 'registerGetRouteData')]
public function itRegistersGetRoute(Route $route, array $expectedResult)
{
$this->router->get($route);
$this->assertEquals($expectedResult, $this->router->getRoutes());
}
My data provider class:
class RouterDataProvider
{
public static function registerGetRouteData(): array
{
return [
$route = new Route('/', ['IndexController', 'index']),
[
'GET' => [
'/' => $route,
],
'POST' => []
]
];
}
}
How could I get this test to run with the desired provider method?
By running PHPUnit with the following flags, I was able to see exactly what my issue was:
./vendor/bin/phpunit --display-deprecations --display-warnings --diplay-errors --display-notices
The data set was invalid. Changing the return to yield and updating the return type for the registerGetRouteData method from array to \Generator resolved this.
I was running phpunit with the --testdox flag, so I'm not sure if this is what stopped me seeing any errors initially and assume the test was being skipped.
For the automated testing of a laravel API I am using the 'laravel' action on github actions, the one made by github actions.
The tests keep failing telling me invalid JSON returned from route, expected response code 200 but got 500, cannot read property status on null and cannot find in json
I'm using laravel sanctum. Could it be a csrf-token problem?
My action yml: https://gist.github.com/I2EJeffrey/77df8faac1b0f86623e2e4449f98d858
My response function:
* success response method.
*
* #return \Illuminate\Http\Response
*/
public function sendResponse($result, $message, $code = 200)
{
$response = [
'success' => true,
'data' => $result, // result is most often one or 2 arrays
'message' => $message,
];
return response()->json($response, 200);
}
Example test:
public function testSuccessfullyCreateAccommodationType()
{
$this->login(); // Login function that lots of tests need.
$response = $this->postJson('/api/v5/accommodations/1/types', ['accommodation_name'=>$this->createName()]);
$response
->assertJsonFragment(['success' => true])
->assertJsonStructure(['success', 'data' =>
[], 'message']); // The array is filled with keys
}
EDIT: 2 errors that I got by using withoutExceptionHandling: https://gist.github.com/I2EJeffrey/da23bfbdf5fba155456bd799a34f6276
EDIT 2: I also get the following warning: TTY mode requires /dev/tty to be read/writable.
EDIT 3: The client model and the client seeder. Whenever I run the tests a mysql docker container starts that gets a db migrated and seeded into it:
https://gist.github.com/I2EJeffrey/39c779df217c9a75a7569f6fa3957d77
https://gist.github.com/I2EJeffrey/41cbbdba8025d38547059d3a6f4d4392
My problem was that the DB didn't get seeded due to not having added $this->call(ClientSeeder::class); to the DatabaseSeeder. Which caused the routes to return null and thus wrong json.
I built an API using dingo/api 0.10.0, Laravel 5.1 and lucadegasperi/oauth2-server-laravel": "^5.1".
All my routes work fine in Postman/Paw!
The problem appears when I try to test the API using PHPUnit.
This is part of my route-api.php file
<?php
$api = app('Dingo\Api\Routing\Router');
$api->version(['v1'], function ($api) {
$api->post('oauth/access_token', function () {
return response(
\LucaDegasperi\OAuth2Server\Facades\Authorizer::issueAccessToken()
)->header('Content-Type', 'application/json');
});
$api->group(['middleware' => ['oauth', 'api.auth']], function ($api) {
$api->post('/register', 'YPS\Http\Controllers\Api\UserController#register');
});
And this is my test file UserRegistrationTest.php
class UserRegistrationTest extends ApiTestCase
{
public function setUp()
{
parent::setUp();
parent::afterApplicationCreated();
}
public function testRegisterSuccess()
{
$data = factory(YPS\User::class)->make()->toArray();
$data['password'] = 'password123';
$this->post('api/register', $data, $this->headers)
->seeStatusCode(201)
->seeJson([
'email' => $data['email'],
'first_name' => $data['first_name'],
'last_name' => $data['last_name'],
]);
}
public function testRegisterMissingParams()
{
$this->post('api/register', [], $this->headers, $this->headers, $this->headers)->seeStatusCode(422);
}
}
The ApiTestCase simply retrieves a token and sets the headers.
private function setHeaders()
{
$this->headers = [
'Accept' => 'application/vnd.yps.v1+json',
'Authorization' => 'Bearer ' . $this->OAuthAccessToken,
];
}
Now, the weird part is that the first test testRegisterSuccess runs perfectly and returns the response I expect. But the second one testRegisterMissingParams, even though it's the same route, returns this,
array:2 [
"message" => "The version given was unknown or has no registered routes."
"status_code" => 400
]
I tracked the error and it is in the Laravel adapter here:
public function dispatch(Request $request, $version)
{
// it seems that the second time around can't find any routes with the key 'v1'
if (! isset($this->routes[$version])) {
throw new UnknownVersionException;
}
$routes = $this->mergeExistingRoutes($this->routes[$version]);
$this->router->setRoutes($routes);
return $this->router->dispatch($request);
}
And further more, if i run one test at a time (eg comment one out, run test and then comment the other and run test) i see the result expected in both tests. The problem is when i run multiple tests.
Any thoughts on that?
Thank you!
Run php artisan api:routes to see full path you may have missed something for the URL, also if this working if you request your URL manually?
I had same problem with testing using Dingo & Lumen. This worked for me - remove bootstrap="bootstrap/app.php" from phpunit.xml file and change line processIsolation="false" to processIsolation="true".
I'm sort of new to Silex and learning. I'm trying to return the base url inside one of my route controllers to return the new path after inserting a row into a database. No matter what I try it's returning an empty string though. Here's part of that function:
$app->match('/item', function(Request $request) use ($app) {
$method = $request->getMethod();
switch ($method) {
//POST
case 'POST': //insert
$data = array(
'item' => $request->get('item'),
'description' => $request->get('description'),
'quantityOnHand' => $request->get('quantityOnHand'),
'reorderPoint' => $request->get('reorderPoint'),
'supplier_id' => $request->get('supplier_id')
); //before to get supplier_id???? Or do it in ios
$app['db']->insert('inventory', $data);
$newId = (int) $app['db']->lastInsertId(); //cast int
$location = $request->getBaseUrl().'/inventory/id/'.$newId;
return $app->json(array('status' => 201, 'id'=>$newId, 'location' =>$location), 201);
break;
}
}
Everything in the $location variable is working except the base path. Am I missing something? I'm injecting the $request into the controller. When I run this it returns /inventory/item/101 for the location, without my base url.
Based on comments, it seems that what the OP is looking for is the hostname, not the base url, so the getHost method should be used.
Remember, though, that to generate URLs easily you should use the UrlGenerator instead of crafting it manually. Silex has a default provider for this service.
I'm having some trouble using Laravel's Input::replace() method to simulate a POST request during unit testing.
According to Jeffrey Way here and here, you can do something like this:
# app/tests/controllers/PostsControllerTest.php
public function testStore()
{
Input::replace($input = ['title' => 'My Title']);</p>
$this->mock
->shouldReceive('create')
->once()
->with($input);
$this->app->instance('Post', $this->mock);
$this->call('POST', 'posts');
$this->assertRedirectedToRoute('posts.index');
}
However, I can't get this to work. Input::all() and all Input::get() calls still return an empty array or null after Input::replace() is used.
This is my test function:
public function test_invalid_login()
{
// Make login attempt with invalid credentials
Input::replace($input = [
'email' => 'bad#email.com',
'password' => 'badpassword',
'remember' => true
]);
$this->mock->shouldReceive('logAttempt')
->once()
->with($input)
->andReturn(false);
$this->action('POST', 'SessionsController#postLogin');
// Should redirect back to login form with old input
$this->assertHasOldInput();
$this->assertRedirectedToAction('SessionsController#getLogin');
}
The $this->mock->shouldReceive() doesn't get called with $input though - it only gets an empty array. I've confirmed this in the debugger by looking at Input::all() and Input::get() for each value, and they're all empty.
TL/DR: How do I send a request with POST data in a Laravel unit test?
You should use Request::replace(), not Input::replace in order to replace input data for the current request.