I have one issue with testing and I hope you can help me.
I have a scheduled job that collects information about others websites. it is a web crawler. This is the job:
<?php
namespace App\Jobs;
use App\Models\Post;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
class FetchPostsJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $baseUrl = 'https://post.com';
public function handle()
{
ini_set('max_execution_time', 180);
$response = Http::get($this->baseUrl.'/posts');
$htmlString = $response->body();
//add this line to suppress any warnings
libxml_use_internal_errors(true);
$doc = new \DOMDocument();
$doc->loadHTML($htmlString);
$xpath = new \DOMXPath($doc);
$post_links = $xpath->evaluate('//div[@]//p//a');
$current_posts = Post::pluck('link')->toArray();
$posts_array = [];
$now = now()->toDateTimeString();
foreach ($post_links as $link) {
$post_url = $this->baseUrl.$link->getAttribute('href');
if (!in_array($post_url, $current_posts) ) {
$posts_array[] = $this->getPostItem($post_url, $now);
}
}
Post::insert($posts_array);
}
public function getPostItem($post_url, $now)
{
$response = Http::get($post_url);
$htmlString = $response->body();
libxml_use_internal_errors(true);
$doc = new \DOMDocument();
$doc->loadHTML($htmlString);
$xpath = new \DOMXPath($doc);
$post_title = $xpath->evaluate('//h1')->item(0)->textContent;
$post_content = $xpath->evaluate('//div[@]')->item(0);
return [
'title' => $post_title,
'content' => $post_content,
'link' => $post_url,
'slug' => Str::slug($post_title. rand(1, 100)),
'created_at' => $now,
'updated_at' => $now,
];
}
}
Then I created test like this:
<?php
namespace Tests\Unit;
use Tests\TestCase;
use App\Jobs\FetchPostsJob;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Queue;
class FetchPostsJobTest extends TestCase
{
use RefreshDatabase;
public function test_post_job_is_dispatched()
{
Bus::fake();
$this->artisan('schedule:work');
Bus::assertDispatched(FetchPostsJob::class);
}
public function test_it_queues(){
Queue::fake();
$this->artisan('schedule:work');
Queue::assertPushedOn('fetch-posts', FetchPostsJob::class);
}
public function test_it_stores_data_in_database(){
Bus::fake();
$this->artisan('schedule:work');
$this->assertGreaterThan(Post::count(), 0);
}
}
app/Console/Kernel.php
<?php
namespace App\Console;
use App\Jobs\FetchPostsJob;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->job(new FetchPostsJob, 'fetch-posts')->everyThreeMinutes();
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}
But the tests runs forever, none of the tests works properly, what can I do? thank you.
CodePudding user response:
Here's my suggestion on how to test this. If you have a different URL you can be testing on that would be even better but the basic use case would be something like:
public function test_it_stores_data_in_database() {
// Fake the HTTP helper
Http::fake([
'*' => Http::response(
'html><body><div><h1>Post title</h1><div >Post content</div></div></body></html>',
200)
]);
Bus::fake();
// Since getPostItem uses HTTP to get the pages you know that the response will be the above
app()->make(FetchPostsJob::class)->getPostItem('example.com', Carbon::now());
$this->assertGreaterThan(Post::count(), 0);
// You can also assert that the database contains a post with title "Post title" and content "Post content" since you control how the HTML for the test will look like
}