質問

I'm attempting to test relationships between models using Ardent and FactoryMuff. I'm able to test the relationship in the belongs_to direction, but I'm having trouble testing it in the has_many direction.

The models I'm testing are a residential real estate rental application and it's corresponding rental history. A very simplified db schema:

+--------------+
| applications |
+--------------+
| id           |
| name         |
| birthday     |
| income       |
+--------------+

+----------------+
| history        |
+----------------+
| id             |
| application_id |
| address        |
| rent           |
+----------------+

This is my history model:

class History extends Ardent
{
    protected $table = 'history';

    public static $factory = array(
        'application_id' => 'factory|Application',
        'address' => 'string',
        'rent' => 'string',
    );

    public function application()
    {
        return $this->belongsTo('Application');
    }
}

This is my test to make sure that a history object belongs to a rental application:

class HistoryTest extends TestCase
{
    public function testRelationWithApplication()
    {
        // create a test rental history object
        $history = FactoryMuff::create('History');

        // make sure the foreign key matches the primary key
        $this->assertEquals($history->application_id, $history->application->id);
    }

}

This works just fine. However, I can't figure out how to test the relationship in the other direction. In the project requirements, a rental application MUST have at least one rental history object associated with it. This is my application model:

class Application extends Ardent
{
    public static $rules = array(
        'name' => 'string',
        'birthday' => 'call|makeDate',
        'income' => 'string',
    );

    public function history()
    {
        return $this->hasMany('History');
    }

    public static function makeDate()
    {
        $faker = \Faker\Factory::create();
        return $faker->date;
    }
}

This is how I'm attempting to test the has_many relationship:

class ApplicationTest extends TestCase
{
    public function testRelationWithHistory()
    {
        // create a test rental application object
        $application = FactoryMuff::create('Application');

        // make sure the foreign key matches the primary key
        $this->assertEquals($application->id, $application->history->application_id);
    }
}

This results in ErrorException: Undefined property: Illuminate\Database\Eloquent\Collection::$application_id when I run my unit tests. It makes sense to me. Nowhere have I told FactoryMuff to create at least one corresponding History object to go along with my Application object. Nor have I written any code to enforce the requirement that an Application object must have at least one History object.

Questions

  1. How do I enforce the rule "an application object MUST have at least one history object"?
  2. How do I test the has_many direction of the relationship?
役に立ちましたか?

解決

Yes, you should always test relationships in both directions, as it's feasible that you would require access to each model respectively from both ends.

As for enforcing, there is no programmatic way that I am aware of, you'd have to do it inline when you call the model, using the has() method:

$application = Application::find($id)->has('history', '>=', 1)->get();

Now the hasMany() relationship will return a collection, so you're actually trying to access the application_id property on an instance of Collection.

You have two choices to test this, the first would be to loop through and assertEquals like below:

foreach($application->history as $history) {
    $this->assertEquals($application->id, $history->application_id);
}

Now, since you're testing, and the instances of the models created are for testing purposes, then it may be worth doing something like the following:

$history = $application->history->first();
$this->assertEquals($application->id, $history->application_id);

To reiterate, the problem is that the hasMany() relationship will always return an instance of Illuminate\Database\Eloquent\Collection if child relationships are found, even if there is only one, but the two above methods should suffice for your needs. Hope that helps.

P.S: My examples do not include validation to make sure variables aren't null and so on and so forth, may be worth adding that into your tests.

-- UPDATE --

I'm not familiar with FactoryMuff unfortunately, although if it works the way that I believe, you should be able to do the following in your tests:

$application = FactoryMuff::create('Application');
$history = FactoryMuff::create('History');
$application->history()->save($history);

If you're referring to your actual application code, you can just hook into the model events and add a new History object then and there.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top