rhand's avatar
Level 6

Faker Invoice Creation not running

Working on a test to create an invoice and check that it has been created

For the test I made tests/Feature/Http/Controllers/DashboardController/DownloadInvoicesTest.php with:

<?php

namespace Tests\Feature\Http\Controllers\DashboardController;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class DownloadInvoicesTest extends TestCase
{
    /**
     * Test the downloadInvoices method.
     *
     * @return void
     */
    public function testDownloadInvoices()
    {
        // Create an invoice
        $invoice = \App\Models\Invoice::factory()->create();
        
        // Make a GET request to the route that triggers the downloadInvoices method
        $response = $this->get('/downloadInvoices');

        // Assert that the response status is 200
        $response->assertStatus(200);

        // Assert that the response has headers indicating a file download
        $response->assertHeader('Content-Disposition', 'attachment; filename=invoices.pdf');
    }
}

I also added a factory database/factories/InvoiceFactory.php

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Invoice>
 */
class InvoiceFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition()
    {
        return [
            'invoice_no' => $this->faker->unique()->numerify('INV#####'),
            'order_id' => $this->faker->unique()->randomNumber(),
            'order_id' => $this->faker->unique()->randomNumber(),
            'paid_at' => $this->faker->date(),
            'payment_id' => $this->faker->unique()->randomNumber(),
            'status' => 'paid',
            'domain_order_id' => $this->faker->unique()->randomNumber(),
            // Add other fields as necessary...
        ];
    }
}

and I updated the model app/Models/Invoice.php to work with factories:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Invoice extends Model
{
    use HasFactory;

    protected $fillable = [
        'invoice_no', 'order_id', 'user_id', 'paid_at', 'status',
        'payment_id', 'domain_order_id',
    ];

    protected $casts = [
        'paid_at' => 'datetime',
    ];

    public function order()
    {
        return $this->belongsTo(\App\Order::class);
    }

    public function domainOrder()
    {
        return $this->belongsTo(\App\Models\DomainOrder::class, 'domain_order_id');
    }

    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::created(function ($invoice) {
            $invoice->invoice_no = str_pad($invoice->id, 5, 0, STR_PAD_LEFT).date('dmY');
            $invoice->save();
        });
    }
}

Issue is that verification fails and not data is added to Invoice tabel at mysql_testing database

DB_CONNECTION=mysql_testing
DB_HOST=localhost
DB_DATABASE=app_test
DB_USERNAME=root
DB_PASSWORD=
DB_PORT=3306

in .env.testing

The method to run invoices is

public function downloadInvoices()
    {
        $params = request()->invoices ?: [];
        $path = storage_path().'/invoices/'.Auth::user()->id;
        if (! count($params)) {
            return redirect()->back()->with('exception', 'Please select invoices');
        }

        // Clean old data and create folder
        \File::deleteDirectory($path);
        if (! file_exists($path)) {
            mkdir($path, 0777, true);
        }
        // Init zip file
        $zip = Zip::create($path.'/invoices.zip');
        // Create pdf invoice
        // https://ourcodeworld.com/articles/read/687/how-to-configure-a-header-and-footer-in-dompdf
        $lastId = Order::orderBy('id', 'DESC')->first()->id ?? 0;
        foreach ($params as $param) {
            if ($param) {
                $pathPdf = '';
                $ids = explode('-', $param);
                $orderId = $ids[0];
                $invoiceId = $ids[1];

                $order = Auth::user()->orders()->where('id', $orderId)->first();
                $invoice = Invoice::where('id', $invoiceId)->first();
                $orderDate = $invoice->paid_at;
                $dateOnly = explode(' ', $orderDate);
                unset($dateOnly[1]);
                $dateOnly = implode(' ', $dateOnly);
                $dateOnly = str_replace('-', '', $dateOnly);
                $invoiceNumber = $lastId.$dateOnly;
                // Debug by uncommenting. Will display html in browser instead of download as pdf
                // return view('pdfs.invoice', [
                //     'order' => $order,
                // 'invoice'=>$invoice,
                //     'plan' => $order->plan,
                //     'extraSpace' => $order->extra_space,
                //     'project' => $order->project,
                //     'invoiceNumber' => $invoiceNumber,
                // ]);
                $pdf = \PDF::loadView('pdfs.invoice', [
                    'order' => $order,
                    'invoice'=>$invoice,
                    'plan' => $order->plan,
                    'extraSpace' => $order->extra_space,
                    'project' => $order->project,
                    'invoiceNumber' => $invoiceNumber,
                ]);

                $pathPdf = $path.'/appname-'.Str::slug($order->project->name, '-').'-'.$invoice->id.'-'.date('d-m-Y').'.pdf';
                $pdf->setWarnings(true)->save($pathPdf);
                // Add to zipfile
                $zip->add($pathPdf);
            }
        }

        $zip->close();

        return response()->download($path.'/invoices.zip');
    }

What am I doing wrong running the faker? And what am I doing wrong verifying a created invoice?

0 likes
2 replies
LaryAI's avatar
Level 58

It seems like there are a few issues that need to be addressed in your code. Let's go through them step by step.

  1. Invoice Factory Duplicate Field: In your InvoiceFactory, you have defined the order_id field twice. You should remove the duplicate and ensure that all necessary fields are defined only once.

  2. Database Testing Configuration: Make sure that your .env.testing file is correctly set up and that the DB_CONNECTION is pointing to a test database that is different from your production database. Laravel should automatically use the .env.testing file when running PHPUnit tests.

  3. Database Migrations: Ensure that you have run the migrations for the test database. You can use the RefreshDatabase trait in your test to automatically handle this:

use Illuminate\Foundation\Testing\RefreshDatabase;

class DownloadInvoicesTest extends TestCase
{
    use RefreshDatabase;
    
    // ... rest of your test code
}
  1. Testing the Download Response: When testing the download response, you are asserting that the file download is a PDF. However, your controller method is creating a ZIP file. You should assert the correct file type and name:
$response->assertHeader('Content-Disposition', 'attachment; filename=invoices.zip');
  1. Testing Without Session Data: Your downloadInvoices method expects request()->invoices to be an array of selected invoices. In your test, you are not providing this data. You need to simulate a request that includes this data:
$response = $this->withSession(['invoices' => [$invoice->id]])->get('/downloadInvoices');
  1. Authentication: Your downloadInvoices method uses Auth::user() which means it expects a logged-in user. In your test, you are not simulating a user session. You should create a user and simulate a login:
$user = \App\Models\User::factory()->create();
$response = $this->actingAs($user)->withSession(['invoices' => [$invoice->id]])->get('/downloadInvoices');
  1. Model Boot Method: In your Invoice model's boot method, you are modifying the invoice_no after the invoice is created. This could cause an issue if the invoice_no is supposed to be unique and is used in your test assertions. Make sure that this logic is correct and does not interfere with your tests.

  2. Testing Environment: Ensure that your tests are running in the testing environment. You can set the environment to testing by setting the APP_ENV variable in your .env.testing file:

APP_ENV=testing

After addressing these issues, your test should look something like this:

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class DownloadInvoicesTest extends TestCase
{
    use RefreshDatabase;

    public function testDownloadInvoices()
    {
        $user = \App\Models\User::factory()->create();
        $invoice = \App\Models\Invoice::factory()->create();

        $response = $this->actingAs($user)->withSession(['invoices' => [$invoice->id]])->get('/downloadInvoices');

        $response->assertStatus(200);
        $response->assertHeader('Content-Disposition', 'attachment; filename=invoices.zip');
    }
}

Make sure to adjust your test and controller code according to the actual requirements of your application.

rhand's avatar
Level 6

This test

<?php

use App\Models\Auth\Role;
use App\Models\Auth\User;
use App\Models\Company;
use App\Models\Invoice;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;

class DownloadInvoicesTest extends TestCase
{
    // use RefreshDatabase;

    public function testDownloadInvoices()
    {

        // Create a company
        $company = Company::factory()->create();

        // Create a role
        $role = Role::factory()->create();

        // Create a user with the company_id and role_id set to the IDs of the company and role you just created
        $user = User::factory()->create(['company_id' => $company->id, 'role_id' => $role->id]);
        // $user = User::factory()->create();
        $invoice = Invoice::factory()->create();

        $response = $this->actingAs($user)->withSession(['invoices' => [$invoice->id]])->get('/downloadInvoices');

        $response->assertStatus(200);
        $response->assertHeader('Content-Disposition', 'attachment; filename=invoices.zip');
    }
}

is getting there, but seems the Invoice factory is not keeping data so cannot see if factory work this way. Result is now not showing data invoice zipped file is added but perhaps I look for the wrong file name still.

• DownloadInvoicesTest > download invoices
  Expected response status code [200] but received 302.
  Failed asserting that 200 is identical to 302.

  at tests/Feature/Http/Controllers/DashboardController/DownloadInvoicesTest.php:31
     27▕         $invoice = Invoice::factory()->create();
     28▕ 
     29▕         $response = $this->actingAs($user)->withSession(['invoices' => [$invoice->id]])->get('/downloadInvoices');
     30▕ 
  ➜  31▕         $response->assertStatus(200);
     32▕         $response->assertHeader('Content-Disposition', 'attachment; filename=invoices.zip');
     33▕     }
     34▕ }
     35▕ 

Running the factory did work:

php artisan tinker
Psy Shell v0.11.22 (PHP 8.3.0 — cli) by Justin Hileman
> DB::setDefaultConnection('mysql_testing');
= null

> Invoice::factory(5)->create();
[!] Aliasing 'Invoice' to 'App\Models\Invoice' for this Tinker session.
= Illuminate\Database\Eloquent\Collection {#8138
    all: [
      App\Models\Invoice {#8143
        invoice_no: "0000103122023",
        order_id: 89733,
        paid_at: "1998-09-30 00:00:00",
        payment_id: 8,
        status: "paid",
        domain_order_id: 0,
        updated_at: "2023-12-03 10:27:03",
        created_at: "2023-12-03 10:27:03",
        id: 1,
      },
...

I realize I will need more. I need to perhaps create an order, plan , project, then the session , session data and then request. Going to take some time for me.

Please or to participate in this conversation.