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

Testing databases that are not controlled by your domain #60

Open
nathan-isaac opened this issue Jun 2, 2017 · 1 comment
Open

Testing databases that are not controlled by your domain #60

nathan-isaac opened this issue Jun 2, 2017 · 1 comment

Comments

@nathan-isaac
Copy link
Collaborator

nathan-isaac commented Jun 2, 2017

@adamwathan I have a few testing questions for you that I have been struggling with for a while now.

I have two questions.

  • Referenced example 1 below: What is the best approach when testing database calls that are not from your own database? A database that has been created and managed outside of a Laravel framework. This is a database that is custom and on the small side (not many tables).
  • Referenced example 2 below: What is the best approach for building up non Eloquent mock data? It would be nice to use something like the factory() function for non Eloquent models.

1. Database calls to other custom database

Note: In this example the Eloquent models are connected to a non default database connection.

Code examples

I think it would be nice to write a test like this.

/** @test */
public function get_post_data_from_non_default_database_connection()
{
    $post = factory(\App\Other\Post::class)->create();

    $this->getJson('/api/other/posts')
        ->assertStatus(200)
        ->assertJson([
            ['title' => $post->title]
        ])
}

Unfortunately, I can not write something like this because I don't have migrations for this other connection since it is managed separately. Should I create migrations only for testing so I can use Eloquent?

Here is the other solution.

/** @test */
public function get_post_data_from_non_default_database_connection()
{
    $posts = [
        ['title' => 'Cool Title']
    ];

    $repository = Mockery:mock(PostRepository::class)
        ->shouldRecieve('posts')
        ->once()
        ->andReturn($posts)
        ->getMock();

    $this->app->instance(PostRepository::class, $repository);

    $this->getJson('/api/other/posts')
        ->assertStatus(200)
        ->assertJson([
            ['title' => 'Cool Title']
        ])
}

Drawbacks:

  • Here I don't get the assurance that the query is actually working because it is just being mocked.
  • I don't get the nice Eloquent model features.
  • I also don't get the factory() features to build up fake data.

2. Database calls to enterprise database

/** @test */
public function get_transaction_data_from_enterprise_database()
{
    $user = factory(User::class)->create();

    $transactions = [
        ['description' => 'Some fee...']
        // A lot of other columns ...
    ];

    // Mocking complex transaction query
    $repository = Mockery:mock(TransactionsRepository::class)
        ->shouldRecieve('transactions')
        ->once()
        ->andReturn($transactions)
        ->getMock();

    $this->app->instance(TransactionsRepository::class, $repository);

    $this->actingAs($user)
        ->getJson('/api/enterprise/transactions')
        ->assertStatus(200)
        ->assertJson([
            ['description' => 'Some fee...']
            // A lot of other columns ...
        ])
}

I wish I could use some type of dataFactory() function that has a lot of the same features as Laravel's factory() function.

Conclusion

Does all this make sense? Can I explain anything in more detail?

What am I missing? Am I making things to complicated? Should I just deal with it? Just part of me feels like there is a better way. Any feedback is greatly appreciated.

@adamwathan
Copy link
Collaborator

Hey @nathanjisaac!

Please be conscious that when you open an issue on this repo, 2000+ people get an email about it 😬

Happy to try and help but I'd prefer to reserve issue for specific questions about something in the course to make sure we're not abusing peoples' inboxes 😄

That said here's a few thoughts:

Question 1:

I think both options you presented are valid; it really just depends on what you are trying to test.

If you have separate high quality integrated tests for your PostRepository and you are just interested in testing that your routes and controller are formatting and returning the data properly, using a test double for the repository is totally fine.

If you don't have separate tests for the PostRepository or you'd just prefer to write your tests in more of a black box style to give you more freedom to refactor, I think your best bet is to figure out a way to setup a test version of the same database.

I used this package at a previous job once to generate migrations from an existing database:

https://github.com/Xethron/migrations-generator

If I remember right it's a little rough around the edges, but it was good enough to get me something to work with. I wouldn't pull that package into your actual project, just create a new dummy project, pull that package in, point the project at the right DB, then generate the migrations and copy those over to your real project and commit them to the repo. It will probably save you a bit of time vs. doing it all by hand.

Question 2:

I can't quite tell from your example where you'd like to use the dataFactory() function idea you're talking about or what it would do, but you should feel totally free to write any helper functions like that that you need that will make your tests simpler and more expressive.

I think the most important lesson to take away is this:

In Laravel, a lot of work has been done for you to provide lots of what I call "test support" code; features and functionality designed to make it easier for you to write simple, expressive tests.

When you are working on a non-Laravel project or integrating with another system, you should be prepared to invest in writing a lot of your own test support code to make your life easier.

For example, I worked on a project once that relied heavily on Elastic Search. I wanted to be able to easily seed the Elastic Search database in my tests, clear the whole database at the beginning of my test suite, etc; the sorts of things we do in Laravel with factories and the DatabaseMigrations or DatabaseTransactions helpers.

So I spend half a day building out some tooling to make it easier for me to do that, kept it all in a folder called /tests/Support, and continuously updated it and improved as we continued to work on the project and ran into different needs.

So when you run into situations that feel hard or cumbersome to set up, think of ways you can make that easier for yourself by creating your own support code for your test suite. I do it a lot even in Laravel, where most of the basic test support code is already provided for you 👍

Hope that helps!

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

2 participants