megaman7's avatar

Database testing - mocking vs test db

When testing anything which interfaces with things outside of the scope of your test (APIs, databases, file-systems, queues ect) you have a choice of mocking the thing you dont want to test or using a test version of it.

Which you should use differs in each case

My opinion is this. Mocking is usually easier and thus should be your first choice. However if the thing being mocked returns some complex object which cannot easily be defined then using a test version of that real thing should be used.

A slight edge case is things which are not easy to mock such as static classes. If issues like this get in the way and you cant force it to mock them refactoring the code to be more test friendly is a 3rd option.

anyway What is the best way to test Laravel database interactions?

Laravel has a range of database factory functions and eloquent asserts to aid with creating test data and testing it and a range of traits which automatically build and destroy your database at the start and end of the test.

By comparison it does not seam to have any mocking for eloquent.

Is there any particular reason for this?

0 likes
2 replies
D9705996's avatar

My opinion is that if you are choosing to use laravel as your framework then your are also choosing to adopt eloquent or query builder for interacting with your database. I therefore consider these to be in-scope of my tests and use the available RefreshDatabase trait to simplify my tests to keep them readable, actual hit a database (even if it is an in memory sqlite db) which catches more bugs than you would think and generally have less brittle tests.

$this->json('post', '/comment',['title => 'test'])
  -assertStatus(201);

$this->assertCount(1, Comments::all());
$this->assertDatabaseHas('comments', 'title => 'test');

Super easy/quick to write, readable and tells me a lot about what's working of not in my application. More importantly I know that fundamentally my applications public interface is working.

Mocks, stubs, spies, etc have there place but I rarely use them unless it's for a third party api , etc and even then I normally create a fake version of that class that returns a a sample of that response that I swap out of the service container (with an integration test to actual hit the 3rd party to make sure it's data format hasn't changed)

Is this the correct way to test? From a textbook perspective probably not. Do I sleep soundly at night. I certainly do.

By comparison it does not seam to have any mocking for eloquent. Is there any particular reason for this?

I don't think it's needed, adds complexity to the framework and potentially a barrier to entry for newcomers who want to test.

1 like
emiliosa's avatar

Im on the same situation, where I have to analize everytime if I have to mock static methods (can be done, but requires additional effort) or test against local database (sqlite or rdbms). Maybe is a combination of boths, but an interesting defeat for sure. Thanks for this helpful topic, I wish many more comments would done about.

Please or to participate in this conversation.