Laravel factory cannot create because already has one - laravel

I want to create with factory in the test case but i got a validation error it say The name has already been taken. But how? It always refresh database every time the test start run.
The factory
$factory->define(Companies::class, function (Faker $faker) {
return [
'name' => $faker->unique()->company,
'email' => $faker->email,
'website' => $faker->url,
];
});
The test
Storage::fake('local');
$image = UploadedFile::fake()->image('avatar.jpg', 150, 150);
$companies = factory(Companies::class)->create([
'logo' => $image
]);
$company = $companies->toArray();
$this->actingAs($this->user);
$response = $this->postJson('/home/companies/create/add', $company);
$response->assertStatus(302);

i suggest to use the trait RefreshDatabase in your test then you reset the database after each test to avoid side effect.
just on the top of your test class write :
use RefreshDatabase;
Good luck

Related

How do I run a phpunit test on Redis pub/sub?

I'm building a messenger system with Redis publishing on the Laravel end and subscribing on a node server. I would like to test what is stored in the redis pub method using PHPUnit, but I have no idea where to start.
Controller
class MessageController extends Controller
{
public function store(Conversation $conversation, Request $request)
{
$user = Auth::user();
$message = Message::create([
'body' => $request->input('message'),
'conversation_id' => $conversation->id,
'sender_id' => $user->id,
'type' => 'user_message'
]);
$redis = Redis::connection();
$data = new MessageResource($message);
$redis->publish('message', $data);
}
}
Current Test
/** #test */
public function a_user_can_send_a_message()
{
$this->actingAs($user = User::factory()->create());
$message = Message::factory()->make(['sender_id' => $user->id]);
$conversation Conversation::factory()->create();
$response = $this->json('POST', '/api/message/'. $conversation->id, ['message' => $message->body])
->assertStatus(201);
$response->assertJsonStructure([
'data' => [
'body',
'sender',
]
]);
}
Essentially what I'm trying to see is if message has been published on Redis. I'm unsure how to do this, and I think you would probably need to clear the message from Redis after, would you not?
Your test should be like this:
public function test_a_user_can_send_a_message()
{
$redisSpy = Redis::spy();
$redisSpy->shouldReceive('connection')->andReturnSelf();
$this->actingAs($user = User::factory()->create());
$message = Message::factory()->make(['sender_id' => $user->id]);
$conversation = Conversation::factory()->create();
$this->postJson("/api/message/{$conversation->id}", ['message' => $message->body]);
$this->assertDatabaseCount('messages', 1);
$redisSpy->shouldHaveReceived('publish')
->with('message', new MessageResource(Message::first()));
}
As you can see, I have added Redis::spy(); this is going to allow is to "spy" what is called from Redis. You can still mock methods, and we have to do so, because you use Redis::connect(); and then $redis->publish(...), so we will return the spy when connect is called, that is why we do shouldReceive('connection')->andReturnSelf().
At the end of the code, we check that $redis->publish was called with parameters 'message' and a resource with the desired message. Both must match for this assertion to pass, else you will see a mock error.

Laravel Unit Testing for update method not working

I am trying to unit test the update function in a resource controller and I keep getting an error Call to undefined method assertStatus(). Here is my code: $this->withoutExceptionHandling();
$this->withoutExceptionHandling();
$timesheet = factory(TimeSheets::class)->create();
$user = factory(UsersLoginModel::class)->create();
$data = [
'subject' => $this->faker->text,
'subjectmatter' => $this->faker->text,
'description' => $this->faker->text,
];
;
$update = $this->actingAs($user)->json('PATCH','timesheets/update'.$timesheet->id, $data);
$this->assertStatus(200);
$this->assertEquals($data['subject'], $timesheet->subject);
$this->assertEquals($data['subjectmatter'], $timesheet->subjectmatter);
$this->assertEquals($data['description'], $timesheet->description);
The assertStatus assertion, is not on the Illuminate\Foundation\Testing\TestCase class, but on the Illuminate\Foundation\Testing\TestResponse class so in your code you need to use it like so:
$update = $this->actingAs($user)->json('PATCH','timesheets/update'.$timesheet->id, $data);
$update->assertStatus(200);

How to convert object return by laravel model factory create method into array containing model fields?

For example, I have a UserFactory.php
<?php
use App\User;
use Faker\Generator as Faker;
use Illuminate\Support\Str;
$factory->define(User::class, function (Faker $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'email_verified_at' => now(),
'role' => 'USER',
'password' => 'sasdcsdf34', // password
'remember_token' => Str::random(10),
];
});
Now, I can create a user as following
$user = factory(User::class)->create();
Now, How can I convert this $user object into array containing user info like name,email etc without initializing new array and manually assigning every $user object property. ??
I DON'T want to manually assign like following as it is tedious if there are many properties in $user object
$userArray=[
'id' => $user->id,
'name' => $user->name,
'email' => $user->email
]
I have tried this but it creates array containing various other properties and actual values needed are nested inside properties
$userArray=array($user)
You should try using the raw method of factory instead of create.
$user = factory(User::class)->raw();
This should give you an array you can work with.
Try to add something like this to your model class:
public function getArr(){
foreach($this->attributes as $key => $val){
$array[$key] = $val;
}
return $array;
}
If you wish to have this function in every model you could create trait with this function and then just attach it in model class or any class extending it.
You can use json_decode.
// Laravel 7
$userArray = json_decode(factory(User::class)->create(), true);
// Laravel 8
$userArray = json_decode(User::factory()->create(), true);
For Laravel 8, instead of make or create method, use:
User::factory()->raw();
This will return an array

How in feature testing use data from factory?

In laravel 5.8 app with tests I make posting data with some dummy data, like:
$newVoteCategoryRow= [
'id' => null,
'name' => $new_vote_category_row_name,
'meta_description' => 'vote category meta_description on ' . now(),
'meta_keywords' => [ 'vote category meta_description on ' . now(), 'meta_keywords' ],
'active' => true,
'in_subscriptions' => true,
];
$response = $this->actingAs($loggedUser)->post('/admin/vote-categories', $newVoteCategoryRow);
$this->assertCount( $vote_categories_count+1, VoteCategory::all() );
it works ok, but actually I have factory for VoteCategory table in /database/factories/VoteCategoryFactory.php, defined :
<?php
use Faker\Generator as Faker;
use \Cviebrock\EloquentSluggable\Services\SlugService;
use App\VoteCategory;
$factory->define(App\VoteCategory::class, function (Faker $faker) {
$name= 'Vote category ' . $faker->word;
$slug = SlugService::createSlug(VoteCategory::class, 'slug', $name);
return [
'name' => $name,
'slug' => $slug,
'active' => true,
'in_subscriptions' => false,
'meta_description' => $faker->text,
'meta_keywords' => $faker->words(4),
];
});
and my question is if there is a way in post request instead of $newVoteCategoryRow array use my factory, not adding row in database but
reading data from factory for post request ?
to achieve that you just need to use your factory within the test case method:
to create VoteCategory u have to methods, the first one is make and this one will create an instance of VoteCategory without persisting it within the database, and the create method will persist the new VoteCategory within the database.
in your case, you want to create a new instance without adding it to the database, for that you just need to use make:
$newVoteCategoryRow = factory('App\VoteCategory')->make(); // add this line to your test case method.
$response = $this->actingAs($loggedUser)->post('/admin/vote-categories', $newVoteCategoryRow->toArray());
$this->assertCount( $vote_categories_count+1, VoteCategory::all());
for more information, you can check the doc Laravel 5.8: using-factories

Symfony\Component\Debug\Exception\FatalThrowableError

i'm trying to seed data to database using laravel.
this is my factory model
$factory->define(App\product::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'description' => $faker->description,
'price' =>$faker->price,
'image' => $faker->image,
'value' => $faker->value,
'category_id' => $faker->category_id,
'is_voucher' => $faker->is_voucher,
'voucher_id' => $faker->voucher_id,
];
});
in the producttableseed
public function run()
{
$product = factory(product::class, 10)->create();}
and when i run php artisan db:seed
i get this error
Symfony\Component\Debug\Exception\FatalThrowableError : Argument 1 passed to Illuminate\Database\Eloquent\Factory::{closure}() must be an instance of Faker\Generator\Generator, instance of Faker\Generator given
Do you have the line
use Faker\Generator as Faker;
before you define your factory? This, plus changing first line to this
$factory->define(App\product::class, function (Faker $faker) {
works for me.
Also, be careful with
$product = factory(product::class, 10)->create();}
You are seeding 10 database entries here. Your variable name being singular, looks like you might be up for a surprise when you try working with an array that has multiple entries.

Resources