Question

First of all, I use Scala, but any Java approach will probably work.

I have an application with database connection and I want to run my tests and develop my application without modifying the database or if the database is offline.

Say, I have a large class (or module) that connects to the database, does all those things you wanna do, how would I get access to that class, or to its parameters, from outside?

For example, if I wanted the class to run normally but instead of the statement.executeUpdate( sql ), I wanted a println( "Did: " + sql ), without first method ever being called in this test.

Obviously, one way is to simply replace those statements - or to copy the whole file and replacing them. But it's error prone, if I change it back I might forget something. Plus, it's hugely redundant.

How to approach this problem? How to do it with JUnit?

Disclaimer: Please no solutions like "Parameterize your class." I want my constructors to have very few parameters, I don't want to have to specify everything whenever I call it. The test classes are second class citizens in my application and they should have little to no effect on the actual classes / the actual development.

Was it helpful?

Solution

You should define an interface that contains all the database methods in your class. Then, ensure your existing database class implements that interface.

Now you have an interface, you can either mock the class or develop a stub class for testing. The stub class can just print the SQL, or whatever you want. A mock class is more powerful and can be used to ensure your business logic is working correctly.

The final step is to ensure that anywhere that uses the database accepts your interface in the constructor. E.g.

public class ClassThatUsesTheDatabase {

    public ClassThatUsesTheDatabase(DatabaseProvider provider) {
      //...
    }
}

where DatabaseProvider is your interface. This allows you to test ClassThatUsesTheDatabase with your stub or mock. In production, you would construct this class with your concrete implementation.

In my opinion, this is the only sane way to write applications that rely on external resources.


After re-reading your question, I'm concerned about the following paragraph:

Disclaimer: Please no solutions like "Parameterize your class." I want my constructors to have very few parameters, I don't want to have to specify everything whenever I call it. The test classes are second class citizens in my application and they should have little to no effect on the actual classes / the actual development.

Test classes are not second class citizens. They are just as important as your production code, if you're serious about code quality. And yes, you very often have to design your production code to make it suitable for unit testing. This is unavoidable. The benefits are huge, however.

OTHER TIPS

Please no solutions like "Parameterize your class."

Then you are doing it wrong. Your database calls should be in mock objects which are easy to replace with dummy implementations. Chances are, if you're using executeStatement anyway you're vulnerable to SQL Injection. You should rethink your approach.

Personally, I'm with Uncle Bob in that you want all DB interaction to be segregated into a class in your program, so that you can replace it on tests with something else. And if you don't want to parameterize, there's still the cake pattern and dependency injection.

Now, if you insist on hard coding a dependency on a library that talks to the database, there's still one thing you can do: mock the database. That might well be hard to do as you have to implement the database interface.

I actually did that on a project, though it was a NoSQL database based on HTTP+JSON, so mocking the database was as easy as mocking a web server -- just a couple dozen lines of Unfiltered code to produce all desired responses.

Then just point to the fake database during the tests and you are good to go.

Still, making the code easier to test makes the code better.

With JUnit (and any other mainstream unit testing framework), the standard range of solutions is some sort of dependency injection, which involves "parameterizing your class" as you call it. There is no royal road, sorry :-) However, you can do it in various ways to minimize or avoid disruption of production code.

The usual way to deal with these sorts of problems is to separate the logic to be tested from database access code by extracting the latter and hiding it behind a mockable interface. Then in the code under test, statements like statement.executeUpdate(sql) are replaced by calls like dataAccessor.updateFooBar(foo, bar). And the data accessor object doing these calls is injected into your class in some way - it may be an optional constructor parameter, it may be a field whose default value is the real data accessor object but it has a setter with package access, to set the mock data accessor during unit tests...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top