【问题标题】:unit test for relationship in laravel Modellaravel 模型中关系的单元测试
【发布时间】:2014-09-19 09:40:15
【问题描述】:

我是 laravel 模型单元测试的新手。所以请看看并建议我做错了什么。我的代码如下所示。我有 2 个模型用户和用户状态。

模型用户

public function state()
    {
        return $this->hasOne('UserState');
    }

模型用户状态

public function user()
{
    return $this->belongsTo('User');
}

现在我正在为 UserState 编写单元测试。如下所示:

UnitTest UserStateModelTest

public function testUserRelationIsTrue(){
    $user = new User();
    $user->username = 'testusername';
    $user->save();
    $this->assertEquals($user->user_id, $user->state->id);
}

在 phpunit 运行测试期间会产生错误

Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation:
1452 Cannot add or update a child row: a foreign key constraint fails

【问题讨论】:

  • 您有这些表的迁移脚本可以向我们展示吗?
  • 首先,您正在测试不需要的 Eloquent。其次,您永远不会将state 关联到users,因此它无法通过。最后,$state->user_id == $user->id 给出了关系,你已经设置好了。编辑:还有一件事 - 为您的测试考虑事务或数据库重新创建,或使用内存,而不是使用真正的数据库。
  • @Unnawut 是的,我有。所以我可以在这里粘贴这些文件代码吗
  • 忠实于@JarekTkaczyk 的话,您正在测试 Eloquent,这是一种不好的做法。请记住,正如 Jeffrey Way 所说,您无需开始测试 Eloquent 是否可以保存模型,Tylor 已经测试过了。您需要做的是使用 Mockery 模拟用户模型,并断言它接收到一次“save()”方法。 [Mocking - Laravel Docs] (laravel.com/docs/5.4/mocking) Getting Started with Mockery 这些会让你开始。

标签: php unit-testing laravel phpunit constraints


【解决方案1】:

如果你真的想测试一个关系方法,你可以做到这一点,甚至不需要将模型保存到数据库中。 您仍然需要使用 RefreshDatabase 特征(或 DatabaseMigrations 特征),否则模型将不会映射到任何表。

# tests/Unit/ParentTest.php
/**
 * Test Parent has HasMany relationship with Child model
 * @test
 */
public function has_many_children_with_parent_id_fk()
{
    $parent = new Parent;
    $foreign_key = 'parent_id';

    // Get the relationship object, not the data collection
    $relationship = $parent->children();
    $related_model = $relationship->getRelated();

    // Assert this is a HasMany relationship
    $this->assertInstanceOf(HasMany::class, $relationship);
    // Assert the related model is Child
    $this->assertInstanceOf(Child::class, $related_model);
    // Assert the foreign key is the one we specified
    // (This can be useful if you do not use the default one)
    $this->assertEquals($foreign_key, $relationship->getForeignKeyName());
    // Assert the foreign key is a column
    // of the database table mapped by the Child model
    $this->assertTrue(Schema::hasColumns($related_model->getTable(), array($foreign_key)));
}
# tests/Unit/ChildTest.php
/**
 * Test Child has belongsTo relationship with Parent model
 * @test
 */
public function belongs_to_parent_with_parent_id_fk()
{
    $child = new Child;
    $foreign_key = 'parent_id';

    // Get the relationship object, not the data collection
    $relationship = $child->parent();
    $related_model = $relationship->getRelated();

    // Assert this is a BelongsTo relationship
    $this->assertInstanceOf(BelongsTo::class, $relationship);
    // Assert the related model is Parent
    $this->assertInstanceOf(Parent::class, $related_model);
    // Assert the foreign key is the one we specified
    // (This can be useful if you do not use the default one)
    $this->assertEquals($foreign_key, $relationship->getForeignKeyName());
    // Assert the foreign key is a column
    // of the database table mapped by the Child model
    $this->assertTrue(Schema::hasColumns($relationship->getParent()->getTable(), array($foreign_key)));
}

这有点少,但您可以进行自定义断言以将所有这些都封装在所有测试文件扩展的 TestCase 中。以下方法适合我的需要

# tests/TestCase.php
public function assertHasManyUsing($related_model, $relationship, $foreign_key)
{
    $this->assertInstanceOf(HasMany::class, $relationship);
    $this->assertInstanceOf($related_model, $relationship->getRelated());
    $this->assertEquals($foreign_key, $relationship->getForeignKeyName());
    $this->assertTrue(Schema::hasColumns($relationship->getRelated()->getTable(), array($foreign_key)));
}

public function assertBelongsToUsing($related_model, $relationship, $foreign_key)
{
    $this->assertInstanceOf(BelongsTo::class, $relationship);
    $this->assertInstanceOf($related_model, $relationship->getRelated());
    $this->assertEquals($foreign_key, $relationship->getForeignKeyName());
    $this->assertTrue(Schema::hasColumns($relationship->getParent()->getTable(), array($foreign_key)));
}

现在,重构测试看起来像这样

# tests/Unit/ParentTest.php
/**
 * Test Parent has HasMany relationship with Child model
 * @test
 */
public function has_many_children_with_parent_id_fk()
{
    $parent = new Parent;

    $this->assertHasManyUsing(Child::class, $parent->children(), 'parent_id');
}
# tests/Unit/ChildTest.php
/**
 * Test Child has belongsTo relationship with Parent model
 * @test
 */
public function belongs_to_parent_with_parent_id_fk()
{
    $child = new Child;

    $this->assertBelongsToUsing(Parent::class, $child->parent(), 'parent_id');
}

【讨论】:

    【解决方案2】:

    PHPUnit\Framework\Assert::assertInstanceOf() 的参数 #1 必须是 类或接口名称

    测试\TestCase.php:14

    >      10▕     use CreatesApplication;
    >      11▕
    >      12▕     public function assertHasManyUsing($related_model, $relationship, $foreign_key)
    >      13▕ {  
    >   ➜ 14▕     $this->assertInstanceOf(HasMany::class, $relationship);     
    >      15▕     $this->assertInstanceOf($related_model, $relationship->getRelated());
    >      16▕     $this->assertEquals($foreign_key, $relationship->getForeignKeyName());
    >      17▕     $this->assertTrue(Schema::hasColumns($relationship->getRelated()->getTable(),
    > array($foreign_key)));
    >      18▕ }
    

    【讨论】:

      猜你喜欢
      • 2019-12-11
      • 2015-06-25
      • 2014-07-08
      • 1970-01-01
      • 1970-01-01
      • 2018-03-14
      • 2019-05-14
      • 2019-02-23
      • 1970-01-01
      相关资源
      最近更新 更多