What is the best approach for unit testing when you have interfaces with dummy and real implementations?

I am familiar with the basic principles of TDD:

  • Write down the tests; they will not be executed due to lack of implementation.
  • Write a basic implementation for passing the tests.
  • Refactoring code

However, I got a little confused as to where the interfaces and implementation fit. I am creating a Spring web application in my free time, and instead of blazing cannons, I would like to understand how I can better test interfaces / implementations, take this simple code example that I 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 created the UserService interface, in the end, there will be a real implementation of this that will query the database, however, in order for the application to be disconnected, I replaced the DummyUserService implementation, which will just return some static data.

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

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

Subsequently, I could also create a test class RealUserService that will verify that getUserById() returns the username from the database. This is the part that confuses me a little, does it essentially not exceed the border of a unit test and become a more complex test (with a hit on the database)?

Question (slightly improved): when using interfaces with dummy / encoded and real implementations, which parts should be checked per unit and which parts can be left unchecked?

I spent several hours in this thread on this topic last night and basically found either tutorials on what TDD is, or examples of using JUnit, but nothing in the area of ​​advising on what really needs to be tested. It is possible, however, that I was not looking hard enough or not looking for the right thing ...

+6
source share
3 answers

Do not check fictitious implementations: they will not be used in production. It does not make sense to test them.

If the actual implementation of UserService does nothing but go to the database and get the user name by its identifier, then the test should verify that it does this and does it correctly. Call it an integration test if you want, but nonetheless it is a test that needs to be written and automated.

A common strategy is to populate the database with minimal test data in the annotated @Before test method and check the method to verify that the corresponding username is returned for the identifier that exists in the database.

+4
source

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

In your specific case, you should configure RealUserService using a DB adapter that will perform real database queries. The service itself will be a service, not a persistence of data. Read the book, it will help a lot :)

+3
source

JB Answer is good, I thought I would throw away another method that I used.

When developing an original test, do not worry about disabling the UserService . In fact, go ahead and write the real thing. Follow Kent Beck 3 rules.

1) Make it work. 2) Do it right. 3) Do it fast.

There will be tests in your code that then check the work of find by id. As stated in JB, at this point your tests will be considered integration tests. Once they pass, we have successfully completed step 1. Now let's look at the design. It is right? Change any design odors and check step 2 on your list.

For step 3, we need to quickly complete this test. We all know that integration tests are slow and error prone with all transaction management and database settings. Once we know that the code works, I usually don’t worry about integration tests. It is at this time that you can present your fictitious service, effectively turning your integration test into a unit test. Now, when he does not touch the database in any way, we can check step 3 from the list, because this test now runs quickly.

So what are the problems with this approach? Well, many will say that I still need a test for the UserService database. I usually do not follow integration tests in my project. My opinion is that these types of tests are slow, fragile and do not catch enough logic errors in most projects to pay for themselves.

Hope this helps!

Brandon

0
source

Source: https://habr.com/ru/post/915551/


All Articles