Testing the Laravel Eloquent Model

I am trying to write a test file that checks the association and disconnection between two worm models in Laravel 4.2

Here's my test case:

class BookingStatusSchemaTest extends TestCase
{

  private $statusText = "Confirmed";
  private $bookingStub;
  private $statusStub;

  public function testMigrateService()
  {

    $this->createTestData();

    $booking = $this->bookingStub;
    $status = $this->statusStub;

    /**
     * Check that the booking has no status. OK
     */
    $this->assertNull($booking->status);

    /**
     * Check that status has no booking. OK
     */
    $this->assertEquals(count($status->bookings), 0);

    /**
     * Add a status to the booking. OK
     */
    $booking->status()->associate($this->statusStub);

    /**
     * Check that status has a booking. NOT OK - This gives error
     */
    $this->assertEquals(count($status->bookings), 1);

    /**
     * Check that the booking has a status. OK
     */
    $this->assertNotNull($booking->status);

    /**
     * Do NOT delete the status, just set the reference
     * to it to null.
     */
    $booking->status = null;

    /**
     * And check again. OK
     */
    $this->assertNull($booking->status);
  }

  private function createTestData()
  {

    $bookingStatus = BookingStatus::create([ 
        'status' => $this->statusText 
    ]);

    $booking = Booking::create([ ]);

    $this->bookingStub = $booking;
    $this->statusStub = $bookingStatus;

  }

}

      

When I execute it I get:

There was 1 failure:

1) BookingStatusSchemaTest::testMigrateService
Failed asserting that 1 matches expected 0.

      

Booking model:

class Booking extends Eloquent {

  /**
  * A booking have a status
  */
  public function status()
  {
    return $this->belongsTo('BookingStatus');
  }

}

      

Reservation model:

class BookingStatus extends Eloquent
{
  protected $table = 'booking_statuses';
  protected $guarded = [ 'id' ];
  protected $fillable = ['status'];

  /**
   * A booking status belongs to a booking
   */
  public function bookings()
  {
    return $this->hasMany('Booking');
  }

}

      

Here's a migration diagram for booking:

  Schema::create('booking_statuses', function(Blueprint $table)
  {
    $table->increments('id');
    $table->string('status');
    $table->timestamps();
  });

      

And everything for booking:

Schema::create('bookings', function(Blueprint $table)
{
  $table->increments('id');
  $table->unsignedInteger('booking_status_id')->nullable();
  $table->timestamps();
});

      

What do I need to add / change to test connectivity in my test case?

+3


source to share


2 answers


It's been a while and I completely forgot about this question. Since the OP is still interested in this, I'll try to answer the question in some way.

So my guess is that the actual challenge is: How to check the correct relationship between the two black models?

I think it was Adam Watan who first suggested dropping the terms "Unit Tests" and "Functional Tests" and "I-have-no-idea-what-this-means Tests" and just separate tests for two problems / concepts Features and Units, where Features simply describe the features of the application, such as "Registered user can book a flight", and Units describe the lower-level units and the functionality they expose, such as "Booking has status".

I really like this approach, and with that in mind I would like to refactor your test:

class BookingStatusSchemaTest extends TestCase
{
    /** @test */
    public function a_booking_has_a_status()
    {
        // Create the world: there is a booking with an associated status
        $bookingStatus = BookingStatus::create(['status' => 'confirmed']);
        $booking = Booking::create(['booking_status_id' => $bookingStatus->id]);

        // Act: get the status of a booking
        $actualStatus = $booking->status;

        // Assert: Is the status I got the one I expected to get?
        $this->assertEquals($actualStatus->id, $bookingStatus->id);
    }


    /** @test */    
    public function the_status_of_a_booking_can_be_revoked()
    {
        // Create the world: there is a booking with an associated status
        $bookingStatus = BookingStatus::create(['status' => 'confirmed']);
        $booking = Booking::create(['booking_status_id' => $bookingStatus->id]);

        // Act: Revoke the status of a booking, e.g. set it to null
        $booking->revokeStatus();

        // Assert: The Status should be null now
        $this->assertNull($booking->status);
    }
}

      



This code is untested!

Note how the function names are read as a description of the booking and its functionality. You don't really care about implementation, you don't need to know where and how a Booking gets its BookingStatus - you just want to make sure that if there is a Booking with BookingStatus, you can get that BookingStatus. Or cancel it. Or maybe change it. Or do whatever you want. Your test shows how you want to interact with this device. So write a test and then try doing it.

The main flaw in your test is probably that you are "afraid" of some kind of magic. Instead, think of your models as plain old PHP objects because that's what they are! And you won't run a test like this on POPO:

/**
 * Do NOT delete the status, just set the reference
 * to it to null.
 */
$booking->status = null;

/**
 * And check again. OK
 */
$this->assertNull($booking->status);

      

This is a really broad topic, and every statement about it inevitably implies. There are a few guidelines to help you get along, like "just test your own code", but it's very difficult to put all the sides together. Luckily, the aforementioned Adam Watan has a really great video course called " Test Driven Laravel " where he tests an entire real world Laravel application.It can be a little costly, but it's worth every penny and will help you figure out how to test more than a random dude on StackOverflow :)

+4


source


To verify that you are establishing the correct Eloquent relationship, you need to make assertions on the relationship class ( $model->relation()

). You can argue

  • This is the right type of attitude, claiming that $model->relation()

    is an instance of HasMany

    , BelongsTo

    , HasManyThrough

    ... etc.
  • This refers to the correct model with $model->relation()->getRelated()

  • It is using the correct foreign key with $model->relation()->getForeignKey()

  • The foreign key exists as a column in the table using Schema::getColumListing($table)

    (Here $table

    is either $model->relation()->getRelated()->getTable()

    if this is a relationship HasMany

    or $model->relation()->getParent()->getTable()

    if this is a relationship BelongsTo

    )

For example. Let's say you have a model Parent

and Child

where it Parent

has a lot Child

parent_id

using a method children()

using parent_id

a foreign key. Parent

displays table parents

a Child

- table children

.



$parent = new Parent;
# App\Parent
$parent->children()
# Illuminate\Database\Eloquent\Relations\HasMany
$parent->children()->getRelated()
# App\Child
$parent->children()->getForeignKey()
# 'parent_id'
$parent->children()->getRelated()->getTable()
# 'children'
Schema::getColumnListing($parent->children()->getRelated()->getTable())
# ['id', 'parent_id', 'col1', 'col2', ...]

      

EDIT Also, this is not the case for the database as we never save anything. However, the database must be migrated or the models will not be linked to any tables.

0


source







All Articles