4

I've been reading quite a lot about TDD, especially about various practices and experiences, dos and don'ts and I'm still puzzled about several aspects after trying to apply it on a Spring Boot application with persistence and REST, all packaged by feature.

Many blog posts and answers here on StackOverflow suggest that we should test the interface and not the implementation. However, examples, like those in "Growing Object-Oriented Software, Guided by Tests" by Steve Freeman and Nat Pryce, suggest quite a different approach, since all of them test implementations mostly, asserting number of method calls, etc. So our tests actually become dependent on the implementation itself. Of course, we can use inversion of control - pass the dependencies via constructors and mock them out in our tests, but is there really a point in mocking, let's say, a CRUD repository?

Maybe I'll give you a simple example to better picture this situation. Let's say we have a multipage user information form, and each form page has to be saved separately.

So, the planned structure of the information package could be like this following package-by-feature approach:

com
.. example
.... user
...... information
........ basic
.......... + BasicInformationDto
.......... + BasicInformationService
.......... - BasicInformationServiceImpl
.......... - BasicInformationDao
.......... - BasicInformationRepository
........ additional
.......... + AdditionalInformationDto
.......... + AdditionalInformationService
.......... - AdditionalInformationServiceImpl
.......... - AdditionalInformationDao
.......... - AdditionalInformationRepository
........ + InformationRestController

+ is public and - is default access modifier

My first idea:

  1. Write a simple REST integration test to test mappings provided by InformationRestController.
  2. Create InformationRestController with those mappings.
  3. Create test for BasicInformationService, create a BasicInformationServiceImpl object, mock BasicInformationRepository and test if repository's save method gets called exactly once.
  4. Create classes for BasicInformationService, BasicInformationServiceImpl and BasicInformationRepository with BasicInformationDao.
  5. Refactor, implement details, dto and dao fields, etc.

What if I later decide to use repository`s #saveAndFlush? Is it normal that a unit test needs to be refactored because of such a small change? And if I test only the interface, how do I test different implementations with different dependencies implementing it?

So how, using TDD, would one design, for example, the BasicInformationService`s save method if it only maps a DTO object to a DAO and then persists it using BasicInformationRepository?

Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121
J R
  • 223
  • 3
  • 12
  • 2
    I don't know anything about Spring,but it seems like you've designed all your classes and then tried to develop using TDD. The idea of TDD is that you don't design the classes up front, you let the design emerge as you go. – Mike Stockdale Dec 20 '15 at 01:32
  • Well, I guess this is some kind of a mixed technique, since using the package by approach technique you might foresee such structure or at least keep it in mind while doing TDD. I just really want to know, how people are actually doing TDD, when CRUD repositories come in to play. They're already abstract enough, but we can't really mock them to be realistic. So, how do I design my code with TDD for services that use CRUD repository interfaces inside their implementation because I find tests like "service should return the same id as repository's" not useful at all? – J R Dec 21 '15 at 20:18

1 Answers1

2

Briefly, BDD (behavior-driven development), a refinement of TDD, starts with acceptance tests which test the entire stack, then test-drives details with unit tests as necessary. You can unit-test every class if you want, but you don't have to, and I don't where it doesn't add value. In the case you give, I suspect there would be no need to unit-test most of the components.

It is perfectly normal to target a known design when doing TDD/BDD, either because your framework requires it or because that design is already established in other vertical slices of your program. Only the most extreme TDDers start without a framework. What you don't want to do is make up a big design that your framework doesn't require and that testing and refactoring haven't told you yet that you need.

Community
  • 1
  • 1
Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121