如何创建多态关联工厂用于PHPUnit测试?解决无法创建多评论问题
Alright, let’s tackle your two questions step by step—first getting your test to create multiple comments, then setting up polymorphic factories for your PHPUnit tests.
1. Fixing the "Can’t Create Multiple Comments" Test Issue
Looking at your current test code, you’re only sending one POST request to create a comment, so assertCount(1) is actually working as expected. To create multiple comments, you need to simulate multiple user submissions, and also make sure you’re fetching the fresh version of the article from the database (since the in-memory model won’t auto-update its relationships).
Here’s the adjusted test:
/** @test */ public function an_authenticated_user_can_create_multiple_comments_on_a_post(){ $user = $this->signIn(); $article = factory('App\Article')->create(); // Simulate submitting 3 separate comments for ($i = 0; $i < 3; $i++) { $this->post(route('articles.comments.store', $article->slug), [ 'comment' => $this->faker->sentence, ]); } // Use fresh() to pull the updated article from the database $this->assertCount(3, $article->fresh()->comments); }
Also, double-check your controller’s store method to ensure it’s correctly handling the polymorphic association. It should look something like this (assuming you’ve defined the relationships properly):
// In your CommentsController public function store(Article $article) { auth()->user()->comments()->create([ 'body' => request('comment'), 'commentable_id' => $article->id, 'commentable_type' => Article::class, ]); return back(); } // Or even cleaner, using the article's comments relationship: public function store(Article $article) { $article->comments()->create([ 'body' => request('comment'), 'user_id' => auth()->id(), ]); return back(); }
2. Creating Polymorphic Factories for Tests
Polymorphic associations in factories are straightforward once you set them up correctly. Let’s break this down based on your Article and Comment models.
First, Confirm Your Model Relationships
Make sure your models have the correct polymorphic relationships defined:
// App\Comment.php namespace App; use Illuminate\Database\Eloquent\Model; class Comment extends Model { protected $fillable = ['body', 'user_id']; public function commentable() { return $this->morphTo(); } public function user() { return $this->belongsTo(User::class); } }
// App\Article.php namespace App; use Illuminate\Database\Eloquent\Model; class Article extends Model { protected $fillable = ['title', 'slug', 'body', 'user_id']; public function comments() { return $this->morphMany(Comment::class, 'commentable'); } }
Option 1: Basic Factory with States
Define a base Comment factory, then use states to specify which polymorphic model it should belong to. This keeps your factory flexible.
// database/factories/CommentFactory.php use App\Comment; use App\Article; use App\User; use Faker\Generator as Faker; $factory->define(Comment::class, function (Faker $faker) { return [ 'body' => $faker->paragraph, 'user_id' => factory(User::class), // Associate with a random user ]; }); // State for comments linked to Articles $factory->state(Comment::class, 'for_article', function (Faker $faker) { return [ 'commentable_id' => factory(Article::class), 'commentable_type' => Article::class, ]; });
Usage in Tests:
// Create a single comment linked to an article $comment = factory(Comment::class)->state('for_article')->create(); // Create 5 comments all linked to the same article $article = factory(Article::class)->create(); $comments = factory(Comment::class, 5)->create([ 'commentable_id' => $article->id, 'commentable_type' => Article::class, ]);
Option 2: Auto-Associate with Random Polymorphic Models
If you want your factory to randomly link comments to different polymorphic models (e.g., Article and Post later), you can do this:
// database/factories/CommentFactory.php $factory->define(Comment::class, function (Faker $faker) { // List all possible commentable models $commentableTypes = [Article::class]; // Add others like Post::class later $commentableType = $faker->randomElement($commentableTypes); return [ 'body' => $faker->paragraph, 'user_id' => factory(User::class), 'commentable_id' => factory($commentableType), 'commentable_type' => $commentableType, ]; });
Option 3: Attach Comments to Articles in the Article Factory
You can also set up your Article factory to automatically create comments when generating an article, using afterCreatingState:
// database/factories/ArticleFactory.php use App\Article; use App\User; use Faker\Generator as Faker; $factory->define(Article::class, function (Faker $faker) { return [ 'title' => $faker->sentence, 'slug' => $faker->slug, 'body' => $faker->paragraphs(3, true), 'user_id' => factory(User::class), ]; }); // State to create an article with 3 comments $factory->state(Article::class, 'with_comments', function (Faker $faker) { return []; })->afterCreatingState(Article::class, 'with_comments', function ($article, $faker) { factory(App\Comment::class, 3)->create([ 'commentable_id' => $article->id, 'commentable_type' => Article::class, ]); });
Usage in Tests:
$article = factory(Article::class)->state('with_comments')->create(); $this->assertCount(3, $article->comments);
内容的提问来源于stack exchange,提问作者rakesh shrestha

