Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best approach for Unit testing when you have interfaces with both dummy & real implementations?

I'm familiar with the basic principles of TDD, being :

  1. Write tests, these will fail because of no implementation
  2. Write basic implementation to make tests pass
  3. Refactor code

However, I'm a little confused as to where interfaces and implementation fit. I'm creating a Spring web application in my spare time, and rather than going in guns blazing, I'd like to understand how I can test interfaces/implementations a little better, take this simple example code I've created here :

public class RunMe
{

    public static void main(String[] args)
    {
        // Using a dummy service now, but would have a real implementation later (fetch from DB etc.)
        UserService userService = new DummyUserService();
        System.out.println(userService.getUserById(1));
    }
}

interface UserService
{
    public String getUserById(Integer id);
}

class DummyUserService implements UserService
{
    @Override
    public String getUserById(Integer id)
    {
        return "James";
    }
}

I've created the UserService interface, ultimately there will be a real implementation of this that will query a database, however in order to get the application off the ground I've substituted a DummyUserService implementation that will just return some static data for now.

Question : How can I implement a testing strategy for the above?

I could create a test class called DummyUserServiceTest and test that when I call getUserById() it'll return James, seems pretty simple if not a waste of time(?).

Subsequently, I could also create a test class RealUserService that would test that getUserById() returns a users name from the database. This is the part that confuses me slightly, in doing so, does this not essentially overstep the boundary of a unit test and become more of an intergration test (with the hit on the DB)?

Question (improved, a little): When using interfaces with dummy/stubbed, and real implementations, which parts should be unit tested, and which parts can safely be left untested?

I spent a few hours Googling on this topic last night, and mostly found either tutorials on what TDD is, or examples of how to use JUnit, but nothing in the realms of advising what should actually be tested. It is entirely possible though, that I didn't search hard enough or wasn't looking for the right thing...

like image 314
Jimmy Avatar asked May 12 '12 09:05

Jimmy


People also ask

What are the two approaches to unit testing?

Unit tests can be performed manually or automated. Those employing a manual method may have an instinctual document made detailing each step in the process; however, automated testing is the more common method to unit tests. Automated approaches commonly use a testing framework to develop test cases.

How do interfaces help with unit testing?

When you have an interface, you can easily “hide” any implementation behind it, even a mock for a unit test. An important subject of unit testing is managing external dependencies. The tests should directly cover the unit while using fake replacements (mocks) for the dependencies.

What are best practices for unit testing methods that use cache heavily?

If you want true Unit Tests, then you have to mock the cache: write a mock object that implements the same interface as the cache, but instead of being a cache, it keeps track of the calls it receives, and always returns what the real cache should be returning according to the test case.


2 Answers

Don't test the dummy implementations: they won't be used in production. It makes no real sense to test them.

If the real UserService implementation does nothing else than go to a database and get the user name by its ID, then the test should test that it does that and does it correctly. Call it an integration test if you want, but it's nevertheless a test that should be written and automated.

The usual strategy is to populate the database with minimal test data in the @Before annotated method of the test, and have you test method check that for an ID which exists in the database, the corresponding user name is returned.

like image 50
JB Nizet Avatar answered Oct 06 '22 00:10

JB Nizet


I would recommend you to read this book first: Growing Object-Oriented Software Guided by Tests by Steve Freemand and Nat Pryce. It answers your question and many others, related to TDD.

In your particular case you should make your RealUserService configurable with a DB-adapter, which will make real DB queries. The service itself will the servicing, not data persistence. Read the book, it will help a lot :)

like image 37
yegor256 Avatar answered Oct 05 '22 23:10

yegor256