I am working on an ecommerce project, a generic book shop.
I started out with a Test Driven approach, and I adhered to it fully till now.
Different endpoints on this Lumen Microservice project have been successfully tested earlier to make sure they do CRUD operations. However, as I have to protect the Create, Update and Delete method with token authorisation, I am quite confused how to introduce tests for authorisation.
As of now this is my testing structure:
tests/app/Exceptions/HandlerTest.php
tests/app/Http/Controllers/BooksControllerTest.php
The tests are for index, show, store, update, delete. This is one of the tests:
public function testStoreBookByPost()
{
$book = factory('App\Book')->make();
$this->post(
'/books',
[
'isbn' => $book->isbn,
'title' => $book->title,
'description' => $book->description,
'author' => $book->author,
'image' => $book->image,
'price' => $book->price,
'slug' => $book->slug
]
);
$this
->seeJson(
[
'created' => true
]
)
->seeInDatabase(
'books',
[
'title' => $book->title
]
);
}
I had earlier separated Exception Handler tests, similarly I would prefer to separate the AuthControllerTest to AuthControllerTest.php.
What is the best way to do this?
Do I need to write the authorisation tests by refactoring all the BooksControllerTest?
Or should I just test for issuing of token and inability to manipulate database? Would that be fine?
Short answer: I needed to write the authorisation tests by refactoring all the BooksControllerTest
Long answer: I found out a fantastic way of logging in dummy users during testing.
With that I have created this method.
public function loginWithUserGetJWT()
{
$user = factory('App\User')->create(
[
'password' => bcrypt('366643') // random password
]
);
$content = $this
->post(
'/auth/login',
[
'email' => $user->email,
'password' => '366643'
]
)
->seeStatusCode(200)
->response->getContent();
$token = json_decode($content)->token;
return $token;
}
And I am reusing this method in all the test cases, like so:
public function testStoreBookByPost()
{
$token = $this->loginWithUserGetJWT();
$book = factory('App\Book')->make();
$this->post(
'/books',
[
'isbn' => $book->isbn,
'title' => $book->title,
'description' => $book->description,
'author' => $book->author,
'image' => $book->image,
'price' => $book->price,
'slug' => $book->slug,
'token' => $token
]
);
$this
->seeJson(
[
'created' => true
]
)
->seeInDatabase(
'books',
[
'title' => $book->title
]
);
}
Related
Good Afternoon,
I'm trying to create a Laravel factory where 2 of the 'columns' have the same values every time its called and the rest of the factory can be random.
For instance, I have the following columns in my DB
name
email
phone_number
status_message
status_code
I currently have my factory as follows;
$factory->define(Brand::class, function (Faker $faker) {
return [
'name' => $faker->unique()->company,
'email' => $faker->companyEmail,
'phone_number' => $faker->phoneNumber
];
});
This part works perfectly, as it should, the problem is that each specific status message comes with an individual status code. Is there a way I could add an array of status messages with a status code and have the factory pick a set at random for that record?
The status code / messages are listed below in array format;
[
'3e2s' => 'tangled web',
'29d7' => 'get certified',
'2r5g' => 'art of war',
]
I hope this makes sense. any help would be greatly appreciated.
as i can understand u need to pick random from this array u mentioned in above
$factory->define(Brand::class, function (Faker $faker) {
$data = [
'3e2s' => 'tangled web',
'29d7' => 'get certified',
'2r5g' => 'art of war',
];
$statusCode = array_rand($data);
$statusMessage = $data[$statusCode];
return [
'name' => $faker->unique()->company,
'email' => $faker->companyEmail,
'phone_number' => $faker->phoneNumber,
'status_message' => $statusMessage,
'status_code' => $statusCode,
];
});
I writing tests with Codeception framework.
I am trying to use \Codeception\Stub::update for update method in existing stub, but it isn't work.
$this->userServiceStub = Stub::make(User::class, [
'getService' => function() use ($serviceStub) {
return $serviceStub;
},
'getFields' => [
'ID' => 1234,
'NAME' => 'First Test User',
],
]);
$this->userServiceStub = Stub::update($this->userServiceStub, [
'getFields' => [
'ID' => 1234,
'NAME' => 'Second Test User',
]
]);
When i use getFields method i see old NAME "First Test User". How to use Stub::update correctly?
Seems like this is a bug. Does not work for me either.
I worked around this issue by just recreating the stub from scratch.
I have this nested relation im abit unsure how i assertJson the response within the phpunit test.
FilmController
public function show(string $id)
{
$film = Film::with([
'account.user:id,account_id,location_id,name',
'account.user.location:id,city'
])->findOrFail($id);
}
FilmControllerTest
public function getFilmTest()
{
$film = factory(Film::class)->create();
$response = $this->json('GET', '/film/' . $film->id)
->assertStatus(200);
$response
->assertExactJson([
'id' => $film->id,
'description' => $film->description,
'account' => $film->account->toArray(),
'account.user' => $film->account->user->toArray(),
'account.user.location' => $film->account->user->location->toArray()
]);
}
Obviously this isnt working because its returning every column for the user im a little unfamiliar with how you test nested relations with the code you need so im unsure with a toArray can anyone help out?
Testing is a place where you throw DRY (don't repeat yourself) out and replace it with hard coded solutions. Why? simply, you want the test to always produce the same results and not be bound up on model logic, clever methods or similar. Read this amazing article.
Simply hard code the structure you expect to see. If you changed anything in your model to array approach, the test would still pass even thou your name was not in the response. Because you use the same approach for transformation as testing. I have tested a lot of Laravel apps by now and this is the approach i prefers.
$account = $film->account;
$user = $account->user;
$location = $user->location;
$response->assertExactJson([
'description' => $film->description,
'account' => [
'name' => $account->name,
'user' => [
'name' => $user->name,
'location' => [
'city' => $location->city,
],
],
],
]);
Don't test id's the database will handle those and is kinda redundant to test. If you want to check these things i would rather go with assertJsonStructure(), which does not assert the data but checks the JSON keys are properly set. I think it is fair to include both, just always check the JSON structure first as it would likely be the easiest to pass.
$response->assertJsonStructure([
'id',
'description',
'account' => [
'id',
'name',
'user' => [
'id',
'name',
'location' => [
'id',
'city',
],
],
],
]);
I am trying to write a phpunit test so I can test that I'm getting the correct assertion, this test i currently have which is passing and works as intended.
/**
#test
*/
$filmInfo = $this->postRequest();
$this->assertFilm($this->requestData, $filmInfo['id']);
$film = Film::findOrFail($filmInfo['id']);
$this->assertEquals('LOTR', $filmInfo['name']);
$this->assertEquals('Film about a ring', $filmInfo['description']);
$this->assertEquals('Fantasy', $filmInfo['genre']['main']);
$this->assertEquals('Adventure', $filmInfo['genre']['sub']);
}
This is the request data array it is referring to:
private function requestData(): array
{
return [
'name' => 'LOTR',
'description' => 'Film about a ring',
'main' => 'Fantasy',
'sub' => 'Adventure',
];
}
This test works fine and it passing but I want to test it within one assertion like so:
$this->assertEquals([
'name' => 'LOTR',
'description' => 'Film about a ring',
'genre' => [
'main' => 'Fantasy',
'sub' => 'Adventure'
]
,
], $filmInfo);
But I keep getting an error that the 2 arrays I'm asserting are not matching, do you guys have an idea on what could be causing this?
Just like what the PHPUnit said, your array aren't matching. Your array should match with all values in $filmInfo array.
From your code, we can guessing that you aren't comparing the id. Maybe you can try this code:
$this->assertEquals(array_merge($filmInfo, [
'name' => 'LOTR',
'description' => 'Film about a ring',
'genre' => [
'main' => 'Fantasy',
'sub' => 'Adventure'
],
]), $filmInfo);
Here is my code :
$user = \App\User::where('id', $uid)->firstOrFail();
$user->token()->updateOrCreate(['user_id' => $uid, 'type' => $type], [
'auth' => $auth_token,
'refresh' => $refresh_token,
'type' => $type
]);
I've got two models User and Token with an 'one to one' relationship.
On the first line, I try to catch a User into the database, then I update my model with the updateOrCreate() method.
However, as you can read it, I must use a selector 'user_id' => $uid before to successfully update my model. I think Eloquent should be able to manage it in a different way without making two requests.
You don't have to use the user_id => $uid on that query. It's injected based on the relationship already. You can rewrite that to:
$token = \App\User::where('id', $uid)->firstOrFail()->token()->updateOrCreate(['type' => $type], [
'auth' => $auth_token,
'refresh' => $refresh_token,
'type' => $type
]);