DEV Community


Discussion on: Test Driven Development by Example

adnanhz profile image

Nice Article Nicola! I'd like to add my own little experience with TDD. It also didn't click right away for me.

I usually mostly code web-apps which involve database transactions and few "algorithms" - think for example a blog where you just need to post and fetch articles.

I naturally thought that the best way was to do what Laravel calls "feature tests" which assert the result of an API call.
Then, as I progressed, I learned that in some cases I also need to run tests outside of API calls, so I started writing what would technically be called "Integration tests".

Today, I've reached a stage where TDD has helped me greatly in decoupling my code into small, readable functions and classes.

The final thing I haven't come to a conclusion yet is using mocks to replace external dependencies when testing my code. I think it has to do with the nature of my work which highly involve storing and fetching data to/from the database without much "algorithms" to be tested per se.
I don't think the hassle of implementing something like the repository pattern just to get that extra layer between the database and the ORM so that I can mock/ditch the ORM.
I'm so far still saving myself a good amount of time by spinning up the whole process on a test database and still using the ORM on my "unit" tests. If I had to follow it by the book, I should be mocking every single external dependency.

Do you agree with me, or do you think I haven't caught on writing unit tests with mocks quite yet?

napicella profile image
Nicola Apicella Author • Edited

Thank you!

Your question is interesting and a bit open ended. I'll give you my 2 cents on mocking the datastore (via repository interface or similar).
Most of the code I have seen does use some interface between the business logic and the datastore. And I think the reason is: you want to have a way to test that the code that uses the repository works as expected even at the boundaries; for example: am I catching that SQLException? Do I fallback to a different replica? What happens if the answer from the datastore does not contain the field I was expecting? Can I handle multiple version of the item?
All these things are hard to set up with an integration test.

This is why I think using an interface is useful, but depending on who you ask, you might also get as answers:

  • I use an interface 'cause I do not want to spin up a database to run this test. It's slow.
  • I use an interface 'cause I want to keep the possibility of switching datastore in the future

I do not completely agree with those because:

  • Today, spinning up a database or anything is very cheap - this gives you one less reason to mock it.
  • Without an integration test that covers the database you lose confidence that the program does indeed work
  • Changing datastore is complicated. If you need to do a data migration, not having an interface in place is the least of your problems

To sum it up - it depends on the use case. I tend to prefer a lightweight repository pattern because I can easily test edge conditions.

I hope I answered your question.

adnanhz profile image

Yep, I see where you're getting. Thanks a lot for the input. I'll keep running the database in the foreseeable future even if some people will hate me :D