Kenny Horna
Kenny Horna

Reputation: 14271

Test failing to assert request correctly even when Postman does it right

Good night, I'm having a little trouble here. I'm doing TDD for the first time so I don't know the root of this behavior. This post seems long but mainly because I've copied the mayority of the related code.

Case

Btw, I'm using Laravel as a backend to make an app.

I'm testing that a User with the role admin can create 'Facility' objects but the rest of the users can't. Something really basic.

The problem

When testing the endpoint it let users with the role user (the regular one) to create the object. But when I tried to test it using Postman it worked as it should be: blocking the request.

To manage acl I'm using Laratrust package (it works well, already test it).

Code

routes/api.php // this already has the middlewares: auth & api

Route::post('api/v1/facilities', 'FacilityController@store');

App\Htpp\Controllers\FacilityController.php

use App\Http\Requests\Facility\CreateFacilityRequest;
use App\Http\Resources\FacilityResource;
use App\Repositories\FacilityRepository;
use App\Services\ImageService;
use Illuminate\Http\Response;

// some code

/**
 * Create a new Facility
 *
 * @param CreateFacilityRequest $request
 * @return \Illuminate\Http\JsonResponse
 */
public function store(CreateFacilityRequest $request)
{
    $data = $request->only(['name']);
    $file = $request->file('image');
    $data['image'] = ImageService::storeAs('images/facilities', $file, 'friendly', $data['name']);

    $facility = $this->facilityRepository->create($data);

    return response()->json([
        "message" => "The facility has been added.",
        "data"    => new FacilityResource($facility)
    ], Response::HTTP_CREATED);
}

App\Http\Requests\Facility\CreateFacilityRequest.php

class CreateFacilityRequest extends FormRequest {

    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return auth()->user()->can('create-facilities');
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name'  => 'required|string|unique:facilities,name',
            'image' => 'required|image'
        ];
    }
}

Finally, this is my test:

test\Feature\FacilityTest.php

/**
     * @test
     * Test for: a Facility can be registered only by an admin.
     */
    public function a_facility_can_be_registered_only_by_an_admin()
    {
        /** Given a correct information for a facility */
        \Storage::fake('public');
        $data = ["name" => "Facility A", 'image' => UploadedFile::fake()->image('facility-a.jpg') ];

        /** When the request is made by an admin */
        $admin = $this->createUser([], 'admin');
        $response = $this->apiAs($admin, 'POST','/api/v1/facilities', $data);

        /** Then the facility should be registered */
        $response->assertStatus(Response::HTTP_CREATED); // 201

        /** When the request is made by somebody else */
        $data = ["name" => "Facility B", 'image' =>UploadedFile::fake()->image('facility-b.jpg') ];
        $regular_user = $this->createUser([], 'user');
        $response = $this->apiAs($regular_user, 'POST','/api/v1/facilities', $data);

        /** Then the request should be declined */
        $this->assertTrue($regular_user->hasRole('user'));
        $this->assertFalse($regular_user->can('create-facilities'));

        $response->assertStatus(Response::HTTP_FORBIDDEN); // 403
        \Storage::disk('facilities')->assertMissing('facility-b.jpg');
    }

All the assertions are confirmed except this one:

$response->assertStatus(Response::HTTP_FORBIDDEN); // 403

The output is this:

Expected status code 403 but received 201.

Failed asserting that false is true.

Time: 447 ms, Memory: 20.00MB

FAILURES! Tests: 1, Assertions: 4, Failures: 1.

Process finished with exit code 1


When I dd($response->json()) it return the regular json of a successfull call. But in Postman it returns the correct one:

{
    "message" : "Unauthorized" // with status code 403
}

Someone knows why?


Update

protected function apiAs()

protected function apiAs($user, $method, $uri, array $data = [], array $headers = []) : TestResponse
    {
        $headers = array_merge([
            'Authorization' => 'Bearer '.\JWTAuth::fromUser($user),
            'Accept' => 'application/json'
        ], $headers);

        return $this->api($method, $uri, $data, $headers);
    }


    protected function api($method, $uri, array $data = [], array $headers = [])
    {
        return $this->json($method, $uri, $data, $headers);
    }

Upvotes: 1

Views: 811

Answers (1)

1000Nettles
1000Nettles

Reputation: 2334

This is because you're using jwt-auth whose apiAs() method caches the user's identifier associated to the response on this line:

'Authorization' => 'Bearer '.\JWTAuth::fromUser($user),

This causes your $response variable, the second time you're grabbing the API as a regular user, to return the first cached data for the admin.

I would separate these assertions into separate tests as well as your naming is currently off with a_facility_can_be_registered_only_by_an_admin(). a_facility_cannot_be_registered_by_a_user_that_is_not_an_admin() may be an applicable name and get around this caching.

Upvotes: 2

Related Questions