iampawan31's avatar

How to test a feature which contains relationships

I want to implement a user profile feature. A user has various attributes (Address, work experience,education, user reviews,etc). All these attributes are defined in separate relationships. How do i go about doing this in TDD Environment?

0 likes
8 replies
bobbybouwmann's avatar

It depends on how you want to test this. If you want to test the form you can check if the relation is created on that user object and check if the relation is created in the database

$id = $newUser->id;

$this->seeInDatabase('users', ['id' => $id]);
$this->seeInDatabase('addresses', ['user_id' => $id]);
$this->seeInDatabase('review_user', ['user_id' => $id]);

Now you can run your test and see that the database is not filled with data. You can assign all the info the user when submitting the form and so on.

iampawan31's avatar

@bobbybouwmann Sorry for my noob questions but i am still figuring out how TDD works. So in this case i am not testing any form submission. I have written a feature test to view a newly created user (domain.app/user/{userid}). How do i proceed from here to add the relationship logic? Should i write a unit test for adding and checking the relationships?

rmeza's avatar

@iampawan31 This is not necessary. The unit tests could be used for example to verify the behavior of accesors and mutators, to see if the data is displayed or saved in a certain format for example. With the answer de bobby you can test the feature without problems.

Regards.

bobbybouwmann's avatar

So you basically need to try to get your test passing with the minimum work required and later refactor. So you just need to start adding stuff to do the user to make your tests pass

iampawan31's avatar

@bobbybouwmann Thank you for helping me out with the doubts. I am taking the same path as you have mentioned. But there are couple of questions which makes TDD confusing to me:

  1. Correct me if i am wrong, but we write to unit tests to drive out features right? Let's say i have 10 features on the user profile (Reviews, Availability, Posts published by the user). So for each feature, i need to write unit tests to make them work and the move back to feature test to make it pass?

  2. Also should i consider basic attributes (Name, Location, Educational Qualification, Work Experience) as features?

bobbybouwmann's avatar
Level 88

You don't need to have a unit test to test your feature. The test I showed you are more integration tests, they test more then just a functionality, since they also check the database and insert in the database.

You can for example write a unit test for an example, but your feature is also covering that if you associate the models and check that in the database.

I would probably write tests like this

public function it_tests_if_an_address_can_be_added_to_a_user()
{
    $this->visit('users/create')
        ->fillField('username', 'username');
        ->fillField('password', 'password');
        ->fillField('address[street]', 'Randomstreet');
        ->fillField('address[number]', '123');
        ->fillField('address[city]', 'Randomtown');
        ->click('Submit');

    $this->seeInDatabase('users', ['username' => 'username']);

    // This might be a custom helper
    $user = $this->grabUserByUsername('username');

    $this->seeInDatabase('address', [
        'street' => 'Randomstreet'
        'user_id' => $user->id,
    ]);
}

This is a really basic example for an integration tests, but you still need to do some stuff in your controllers to get it to green. Additionally you can create extra unit tests. Something like this might work for you

public function can_assign_address_to_user()
{
    $user = factory(User::class)->create();

    $address = [
        'street' => 'Randomstreet',
        'number' => 123,
        'city' => 'Randomtown',
    ];
    
    // This might be a method on your user model that you call in your controller. 
    // In your controller $address would be request('address');
    $user->assignAddress($address);

    $this->assertNotNull($user->address);
    $this->assertEquals('Randomstreet', $user->address->street);
}
1 like
iampawan31's avatar

Okay that makes sense. I was under the impression that i need to drive out each feature via unit test first and then test the entire set of features via integration/functional tests. Thanks for clearing it up :)

Please or to participate in this conversation.