Jump to content

Laravel PHPUnit NotFoundHttpException on API route question


Recommended Posts

Hi y'all. First off, posting this here to hopefully avoid the noise that's happening in the Applications sub-forums, so mods please feel free to move if it's too inappropriate here.

Anyway, I'm currently doing a skills assessment for a potential new job in Laravel - if I wasn't using Laravel I would've been done hours ago, but one of the requirements is Laravel. Honestly, for the most part I dig it - it's pretty simple, despite it's reliance on magic. Anyway, I've written a couple API routes and when I visit them in the browser everything works exactly as expected and desired. However, when I run the pre-built test file, I get a Symfony\Component\HttpKernel\Exception\NotFoundHttpException for /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php.

The route as defined in api.php:

use App\Http\Controllers\User;

/**
 * I tried using a Resource Collection here - there's obviously something about those
 * that I'm missing, because it actively refused to do anything worthwhile. I'd pass in
 * UserModel::with('timelogs')->get() like I use in \App\Http\Controllers\User::getTotalSeconds(),
 * and it didn't care. Just printed out absolute garbage, no matter what I fed to it.
 */
Route::get('/user-timelogs', function(Request $request) {
    $u = new User;
    return $u->getTotalSeconds();
});

And the controller code:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\User as UserModel;

class User extends Controller
    public function getTotalSeconds()
    {
        $users = UserModel::with('timelogs')->get();
        $ret = [];
        foreach($users as $user){
            array_push($ret, [
                'user_id' => $user->id,
                'seconds_logged' => $this->calculateTime($user->timelogs)
            ]);
        };
        return json_encode($ret);
    }

    private function calculateTime($logs)
    {
        $totalTime = 0;
        foreach($logs as $log){
            $totalTime += $log->seconds_logged;
        }
        return $totalTime;
    }
}

The model file for good measure:

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password', 'id',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function timelogs()
    {
        return $this->hasMany('App\Timelog');
    }
}

Again, when I visit localhost/api/user-timelogs, the user IDs and correct total of seconds is right there. It's just in the test file that it's an issue. Anybody have any ideas?

Edited by maxxd
was totally not done typing...

I should probably mention that I've been introduced to Laravel but don't code it regularly (I'm much better at SPL than frameworks, which seemed like a good idea until this job hunt), so I'm hoping it's something super simple that I overlooked.

Edited by maxxd
namespace Tests\Unit;
use Tests\TestCase;
class APITest extends TestCase
{
   protected $db;
   const DB_URL = "https://sample.testing.edu/db.json";
   public function setUp(): void
   {
       parent::setUp();
       $this->db = json_decode(file_get_contents(self::DB_URL));
   }
   /**
    * The endpoint under test should return JSON of an array of objects containing users ids
    * and their total time
    * [
    *   {'user_id': <int>, 'seconds_logged': <int>},
    *   ... other users ...
    * ]
    *
    * @test
    */
   public function it_should_provide_sum_of_all_users_time()
   {
       $user = $this->db->users[array_rand($this->db->users)];
       $totalSecondsLogged = array_reduce($this->db->timelogs,
         function ($c, $i) use ($user) {
             return $c + ($i->user_id == $user->id ? $i->seconds_logged : 0);
         }, 0);
       $response = $this->json('GET', '/user-timelogs');
       $response->assertJsonFragment([
         'user_id' => $user->id,
         'seconds_logged' => $totalSecondsLogged,
       ]);
   }
}

There's more to the file, obviously, but this is the test that's  bombing right now. Also, the $this->db stub file contains the same data I'm working with, so there's no issue there, and I think I've rebuilt my composer autoload file like nine or nineteen times, so I'm flat-out stumped. And tired. Mostly tired, but a good portion of that is due to the stumped, so...

Both work from the browser - crap. I forgot about the /api prefix... Let me do some testing.

And it appears as though that was it - I'm gonna update my routes/web.php to match the api.php and see how we do. Thank you!

Edited by maxxd

OK - starting to hate Laravel. I've cloned my repo into a fresh directory, nuked all the existing containers, images, bridges, volumes, etc. and run a fresh docker-compose up. Everything's fine until I get to the migration and seeding for a specific table. Remember, this is the exact set of files I've been developing with, only in a different folder. Now, the controller says save does not exist. Everything else works - I can run the migration and seed just fine on the other three tables/controllers/models, just no this one. Here's the controller:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Issue as IssueModel;

class Issue extends Controller
{

}

Again, this system works perfectly in the other directory, but I need to be able to move it - the reviewers should be able to clone the repo, run docker-compose up and have a functional system. What the actual f**k am I missing? Steps I've run, in order:

composer install

sudo mv .env.example .env
sed -i -e 's/DB_USERNAME=root/DB_USERNAME=laravel/g' .env
sed -i -e 's/DB_PASSWORD=/DB_PASSWORD=laravel/g' .env
sed -i -e 's/DB_HOST=127.0.0.1/DB_HOST=database/g' .env
sed -i -e 's/DB_PORT=3306/# DB_PORT=3306/g' .env

php artisan key:generate

php artisan migrate:refresh --seed

 

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.