Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Factories created in setUp are not removed by the database transaction #9230

Closed
dwightwatson opened this issue Jun 12, 2015 · 5 comments
Closed

Comments

@dwightwatson
Copy link
Contributor

If you have a test case that needs a certain model for every individual test the setup logic is usually places within the setUp() method. However, any models that are created through a factory in the setUp() method are not then removed following each test.

use App\User;
use App\Post;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class PostsControllerTest extends TestCase
{
    use DatabaseTransactions;

    protected $user;

    public function setUp()
    {
        parent::setUp();

        $this->user = factory(User::class)->create();
    }

    public function index()
    {
        $posts = factory(Post::class, 3)->create(['user_id' => $this->user->id]);

        $this->action('GET', 'PostsController@index');

        $this->assertResponseOk();
        $this->assertViewHas('posts');
        $this->assertCount(3, $this->response->getOriginalContent()->getData()['posts']);
    }
}

Following this test the 3 Post models will be removed from the database, however the User model remains. I don't think this is the expected behaviour and can muck up your test suite pretty easily.

If the code from the setup component was moved into each test then the User model is removed as expected.

@shehi
Copy link
Contributor

shehi commented Jun 12, 2015

Just a side note: @dwightwatson , you are not using references, are you? You are manually linking posts to users here. At least I don't think this is correct usage of references with ORM (it will work in SQL-side of course, but ORM generally differs references from generic value assignments).

And if you noticed, the stuff you did in actual test method (adding posts) was reverted after it finished. Everything else outside that method's scope remained (creating User model). When you move User models into the actual method, then it gets reverted as well. Looks to me like PHPUnit related case, not Laravel one.

@dwightwatson
Copy link
Contributor Author

Yeah, I am manually linking the posts to the user. I suppose I could have linked the models using the relationship, but didn't in the example above. It wouldn't make an actual difference in practice, apart from having the attribute fillable.

$user = factory(User::class)->create();
$posts = $user->posts()->saveMany(factory(Post::class, 3)->make());

You could be right, this might be a PHPUnit issue. I don't think it's expected behaviour, the setUp method in most other testing libraries is considered the set up for a test and would be rolled back. I guess one could truncate the table in the tear down or whatever.

It's annoying because this can easily result in the duplication of testing code, or one having to make their own set up method that would need to called manually before each test. I might leave this issue open a little longer as I'd be interested in seeing what other people think about this, whether there is possibly a solution for Laravel or I'm just being pedantic.

@shehi
Copy link
Contributor

shehi commented Jun 12, 2015

Yea, but as I was taught recently in #8459:

Your architecture must support testing units (i.e., classes or very small groups of classes) independently, not all chained together. Otherwise, you have lots of overlap between tests, so changes to one unit can cascade outwards and cause failures everywhere.

http://blog.stevensanderson.com/2009/08/24/writing-great-unit-tests-best-and-worst-practises/

Which technically means tons of rewrites and dupes. For that exact reason, during code quality analysis, certain folders, including tests/ are excluded from reviews and grading as they don't contribute to product quality. The mere task with tests is to make sure the product is working as expected under all and every condition.

@JeffreyWay
Copy link
Contributor

This is happening because your PostsControllerTest's setUp() method is firing before the DatabaseTransition's setup. That's how PHPUnit's @before annotations work. They come after your setUp.

There's a PR to get this fixed in PHPUnit 4.5.

sebastianbergmann/phpunit#1616

@GrahamCampbell
Copy link
Member

Thanks @JeffreyWay. Closing this issue as this is an issue with PHPUnit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants