Skip to main content

Unit-Tests and databases: At which point do I actually connect to the database? [Resolved]

There are answers to the question on how test classes that connect to a database, e.g. "Should service test classes connect ..." and "Unit testing - Database coupled app".

So, in short let's assume you have a class A that needs to connect to a database. Instead of letting A actually connect, you provide A with an interface that A may use to connect. For testing you implement this interface with some stuff - without connecting of course. If class B instantiates A it has to pass a "real" database connection to A. But that means B opens a database connection. That means to test B you inject the connection into B. But B is instantiated in class C and so forth.

So at which point do I have to say "here I fetch data from a database and I will not write a unit test for this piece of code"?

In other words: Somewhere in the code in some class I must call sqlDB.connect() or something similar. How do I test this class?

And is it the same with code that has to deal with a GUI or a file system?

I want to do Unit-Test. Any other kind of test is not related to my question. I know that I will only test one class with it (I so agree with you Kilian). Now, some class has to connect to a DB. If I want to test this class and ask "How do I do this" many say: "Use Dependency Injection!" But that only shifts the problem to another class, doesn't it? So I ask, how do I test the class that really, really establishes the connection?

Bonus question: Some answers here boil down to "Use mock objects!" What does that mean? I mock classes that the class-under-test depend upon. Shall I mock the class-under-test now and actually test the mock (which comes close to the idea of using Template Methods, see below)?

Question Credit: TobiMcNamobi
Question Reference
Asked August 24, 2019
Posted Under: Programming
6 Answers

The Template Method Pattern might help.

You wrap the calls to a database in protected methods. To test this class you actually test a fake object which inherits from the real database connetion class and overrides the protected methods.

This way the actual calls to the database are never under unit tests, that's right. But it's only these few lines of code. And that is acceptable.

credit: TobiMcNamobi
Answered August 24, 2019

The point of a unit test is to test one class (in fact, it should usually test one method).

This means that when you test class A, you inject a test database into it - something self-written, or an lightning-fast in-memory database, whatever gets the job done.

However, if you test class B, which is a client of A, then usually you mock the entire A object with something else, presumably something that does its job in a primitive, pre-programmed way - without using an actual A object and certainly without using a data base (unless A passes the entire data base connection back to its caller - but that's so horrible I don't want to think about it). Likewise, when you write a unit test for class C, which is a client of B, you would mock something that takes the role of B, and forget about A altogether.

If you don't do that, it's no longer a unit test, but a system or integration test. Those are very important as well, but a whole different kettle of fish. To begin with, they are usually more effort to set up and run, it isn't practicable to demand passing them as a precondition to check-ins, etc.

credit: Kilian Foth
Answered August 24, 2019

Unit tests should never connect to a database. By definition, they should test a single unit of code each (a method) in total isolation from the rest of your system. If they don't, then they are not a unit test.

Semantics aside, there are a myriad of reasons why this is beneficial:

  • Tests run orders of magnitude faster
  • Feedback loop becomes instant (<1s feedback for TDD, as an example)
  • Tests can be run in parallel for build/deploy systems
  • Tests don't need a database to be running (makes build much easier, or at least quicker)

Unit tests are a way to check your work. They should outline all of the scenarios for a given method, which typically means all of the different paths through a method. It is your specification that you are building to, similar to double-entry bookkeeping.

What you are describing is another type of automated test: an integration test. While they are also very important, ideally you will have much less of them. They should verify that a group of units integrate with each other properly.

So how do you test things with database access? All of your data access code should be in a specific layer, so your application code can interact with mockable services instead of the actual database. It shouldn't care whether those services are backed by any type of SQL database, in-memory test data, or even remote webservice data. That is not their concern.

Ideally (and this is very subjective), you want the bulk of your code covered by unit tests. This gives you confidence that each piece works independently. Once the pieces are built, you need to put them together. Example - when I hash the user's password, I should get this exact output.

Let's say that each component is made up of roughly 5 classes - you'd want to test all of the points of failure within them. This equates to a lot less tests just to ensure everything is wired properly. Example - test you can find the user from the database given a username/password.

Finally, you want some acceptance tests to actually ensure you are meeting business objectives. There are even less of these; they may ensure the application is running and does what it was built to do. Example - given this test data, I should be able to log in.

Think of these three types of tests as a pyramid. You need a lot of unit tests to support everything, and then you work your way up from there.

credit: Adrian Schneider
Answered August 24, 2019

Testing with external data is integration test. Unit test means you are testing the unit only. It is mostly done with your business logic. To make your code unit testable you have to follow some guidelines, like you have to make your unit independent from other part of your code. During unit test if you need data then you need to forcefully inject that data with dependency injection. There are some mocking and stubbing framework out there.

credit: DeveloperArnab
Answered August 24, 2019
Your Answer