factory not being found in PHP Feature Test Laravel-8 - php

Just playing around with laravel-8 unit tests. I extended the basic TestCase and thought laravels factory method would be available. I checked the composer.json and the factories are being loaded.
I am trying to run this particular test but factory is not found any ideas:
<?php
namespace Tests\Feature\Http\Controllers\Auth;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
use App\User;
class LoginControllerTest extends TestCase
{
use RefreshDatabase;
/** #test */
public function login_authenticates_and_redirects_user()
{
$user = factory(User::class)->create();
$response = $this->post(route('login'), [
'email' => $user->email,
'password' => 'password'
]);
$response->assertRedirect(route('home'));
$this->assertAuthenticatedAs($user);
}
}
The error I am getting is:
1) Tests\Feature\Http\Controllers\Auth\LoginControllerTest::login_authenticates_and_redirects_user
Error: Call to undefined function Tests\Feature\Http\Controllers\Auth\factory()

On laravel 8 models are at 'App\Models\'.
It changes how factory works. See at docs.
So, it should be like:
<?php
namespace Tests\Feature\Http\Controllers\Auth;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
use App\Models\User;
class LoginControllerTest extends TestCase
{
use RefreshDatabase;
/** #test */
public function login_authenticates_and_redirects_user()
{
$user = User::factory->create();
$response = $this->post(route('login'), [
'email' => $user->email,
'password' => 'password'
]);
$response->assertRedirect(route('home'));
$this->assertAuthenticatedAs($user);
}
}

Turns out in upgrading to laravel-8 release notes:
https://laravel.com/docs/8.x/upgrade#seeder-factory-namespaces
"Laravel's model factories feature has been totally rewritten to support classes and is not compatible with Laravel 7.x style factories."
So in order to make it work I used:
$user = \App\Models\User::factory(User::class)->make();

Related

How should I correctly construct my unit test in Laravel

I am quite new to unit testing and could do with a bit of guidance. I am trying to write a simple unit test using the Factory pattern in Laravel and phpunit for an application that allows you to add Companies to a database.
I have a Company Model Company.php, a Factory class which uses it CompanyFactory.php, and finally the unit test itself CompaniesTest.php.
Models/Company.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Company extends Model
{
use HasFactory;
protected $table = 'companies';
protected $fillable = [
'name',
'email'
];
}
Database/Factories/CompanyFactory.php
namespace Database\Factories;
use App\Models\Company;
use Illuminate\Database\Eloquent\Factories\Factory;
class CompanyFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Company::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'name' => $this->faker->name,
'email' => $this->faker->email,
'created_at' => now(),
'updated_at' => now(),
];
}
}
Tests/Feature/CompaniesTest.php
namespace Tests\Unit;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use App\Models\Company;
use Tests\TestCase;
use Illuminate\Support\Str;
use Illuminate\Foundation\Testing\WithFaker;
class CompanyTest extends TestCase
{
use WithFaker, DatabaseTransactions;
public function createCompany($name = NULL)
{
if ($name == NULL) $name = Str::random(6);
$company = Company::factory()->create([
'name' => 'TestName_'.$name
]);
return $company->id;
}
/** #test */
public function company_can_be_created()
{
$name = Str::random(6);
//Create a company
$company_id = $this->createCompany($name);
//Check whether the company name exists in the database
$this->assertDatabaseHas('companies', [
'name' => 'TestName_'.$name
]);
}
}
The test seems to work, but it feels like I might have over-complicated it and probably not followed the correct conventions.
What would be a better way to structure it?
The test looks ok, but what are you actually testing? It seems to me that this test is testing the framework's code, which is actually not what you should do.
Don't test a factory, use it to prepare the data needed before each test. And then run your actual code which you want to test, and assert results.
Update: Continue with CompanyVerifier (see comments). Suppose company can be valid and non-valid. Valid companies can be verified. Then a test may look like:
/** #test */
public function test_valid_company_can_be_verified()
{
// here use a CompanyFactory with some pre-defined data to create "valid" company
$validCompany = $this->createValidCompany();
// here goes the actual code of SUT (system under test)
$verifier = new CompanyVerifier();
$result = $verifier->verify($validCompany);
// here check results
$this->assertTrue($result);
}
The good practice for testing is named AAA (arrange-act-assert). Here the creation of a company with some state is an "arrange" stage. Running tested code is "act". And assert is "assert".
Factories are just a helper for "arrange" stage.

Symfony 5 NormalizerInterface not found

i'm following a tutorial online on how to use Symfony
i followed all the step but for some reason when is try to use NormalizeInterface i get this error :
Cannot determine controller argument for "App\Controller\ApiPostController::index()": the $normalizer argument is type-hinted with the non-existent class or interface: "App\Controller\NormalizerInterface". Did you forget to add a use statement?
I tried multiple solutions and none of them worked
My code is
<?php
namespace App\Controller;
use App\Repository\PostRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer;
class ApiPostController extends AbstractController
{
/**
* #Route("/api/post", name="api_post_index", methods={"GET"})
*/
public function index(PostRepository $postRepository, NormalizerInterface $normalizer )
{
$posts = $postRepository->findAll();
$postsNormalises = $normalizer->normalize($posts, null, ['groups' => 'post:read']);
return $this->render('api_post/index.html.twig', [
'controller_name' => 'ApiPostController',
]);
}
}
Thank you for taking the time to read and thank you for you help in advance
Add use line:
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

PhpStorm collision ignore insteadof

I have this annoying message in my test after updating PhpStorm.
The error says:
Trait method 'beginDatabaseTransaction' will not be applied, because it collides with 'RefreshDatabase'
Why PhpStorm ignores insteadof ?
I there any way to disable this or fix it?
Thanks.
This is the whole test:
<?php
namespace Tests\Feature\Auth\User;
use App\Application\Traits\RefreshDatabaseTransactionLess;
use App\Domain\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Hash;
use Tests\TestCase;
class LoginUserTest extends TestCase
{
use RefreshDatabase, DatabaseMigrations, RefreshDatabaseTransactionless {
RefreshDatabaseTransactionless::beginDatabaseTransaction insteadof RefreshDatabase;
}
protected function postLoginRoute()
{
return route('auth.user.login');
}
public function testUserCanLogin()
{
$password = 'password';
$user = factory(User::class)->create([
'email' => 'test#test.com',
'password' => Hash::make($password)
]);
$response = $this->post($this->postLoginRoute(), [
'email' => $user->email,
'password' => $password
]);
$response->assertSuccessful();
$response->assertJsonStructure([
'token',
'type',
'expires'
]);
}
}
If you are running PhpStorm 2019.3, it's a bug from the new inspection introduced in this release: https://youtrack.jetbrains.com/issue/WEB-43949.
Under certain circumstances the inspection appears to ignore the insteadof keyword.
This is fixed as of (approx) today. Update your IDEs and it should work.

undefined action() in phpunit

<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class MemberRegTest extends TestCase
{
/**
* A basic test example.
*
* #return void
*/
public function testExample()
{
$response = $this->call('GET', '/addmember');
$response = $this->action('GET', 'MemberController#addmember');
}
}
After testing gives me error
Error: Call to undefined method Tests\Feature\MemberRegTest::action()
What i do wrong?
There was an action method inside TestCase back to Laravel 4.x. This method has been replaced for new ones in different classes and packages. (You can confirm this reviewing the Laravel 5.6 TestCase class)
For the latest versions of Laravel, If you are trying to test a HTTP Request you could do:
$response = $this->json('GET', 'api/addmember');
$response->assertStatus(200) // or whatever you want to assert.
Now if you want to do browser tests, you should use the official Laravel Dusk. This package has very cool and useful methods to simulate user interactions with your site, as easy as this:
$this->browse(function ($browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'secret')
->press('Login')
->assertPathIs('/home');

Class Not Found Error in Laravel 5.1

Hey guys I'm trying to learn PHP frameworks as well as OOP and I'm using Laravel 5.1 LTS.
I have the following code in my AuthController
<?php
namespace App\Http\Controllers\Auth;
use App\Verification;
use Mail;
use App\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
class AuthController extends Controller
{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
private $redirectTo = '/home';
public function __construct()
{
$this->middleware('guest', ['except' => 'getLogout']);
}
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:6',
]);
}
protected function create(array $data){
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
// generate our UUID confirmation_code
mt_srand((double)microtime()*15000);//optional for php 4.2.0 and up.
$charid = strtoupper(md5(uniqid(rand(), true)));
$uuid = substr($charid, 0, 8)
.substr($charid, 8, 4)
.substr($charid,12, 4)
.substr($charid,16, 4)
.substr($charid,20,12);
$data['confirmation_code'] = $uuid;
// pass everything to the model here
$setVerification = new Verification();
$setVerification->setVerificationCode($data['email'], $data['confirmation_code']);
// send email for confirmation
Mail::send('email.test', $data, function ($m) use ($data){
$m->from('test#test.com', 'Your Application');
$m->to($data['email'])->subject('Thanks for register! Dont forget to confirm your email address');
});
return $user;
}
}
my error message Class 'Models\Verification' not found is coming from this piece of code here
// pass everything to the model here
$setVerification = new Verification();
$setVerification->setVerificationCode($data['email'], $data['confirmation_code']);
which looks right to my beginner's eyes, but it's clearly wrong.
Here is my Verification class that has the setVerificationCode method
<?php
namespace App\Http\Controllers;
use App\User;
use DB;
use App\Http\Controllers\Controller;
class Verification {
/**
* This method will update the confirmation_code column with the UUID
* return boolean
**/
protected function setVerificationCode($email, $uuid) {
$this->email = $email;
$this->uuid = $uuid;
// check to see if $email & $uuid is set
if (isset($email) && isset($uuid)) {
DB::table('users')
->where('email', $email)
->update(['confirmation_code' => $uuid]);
return TRUE;
} else {
return FALSE;
}
}
/**
* This method will validate if the UUID sent in the email matches with the one stored in the DB
* return boolean
**/
protected function verifyConfirmationCode() {
}
}
Please give the following in AuthController
use App\Http\Controllers\Verification;
instead of
use App\Verification;
If we give use App\Verification , it will check if there is any model named Verification.
its seems that, you are missing something, which, Extend your Model with eloquent model
use Illuminate\Database\Eloquent\Model;
class Verification extends Model
{
and the rest is seems fine.
also share your verification model code
Updated
instead of your this line
use App\Verification;
do this
use App\Models\Verification;
as you created custom directory for your Models then its better to auto load it in your composer.json file. add this line "app/Models" in your "autoload" section. follow this
"autoload": {
"classmap": [
"database",
"app/Models"
],
and after that, run this command in your project repo composer dump-autoload

Categories