Our project have a lot of layers,

  • Entities

  • Repositories

  • UnitOfWorks

  • Domain Services (Business Logic)

  • Validation

  • Infrastructure

  • Application Services


We are only writing unit tests for Business Logic layer and mocking other parts. Is it possible and reasonable to do TDD in this case? (All Business Logic classes consuming other classes via DI with constructor injected dependencies)

  • 3
    Why not? What did you try?
    – Doc Brown
    Jul 4, 2018 at 19:50
  • 2
    It is possible and you can reason about that as core logic is isolated from other parts of application. But there are other approaches can be applied, for example mock only external resources and use actual implementation for when testing feature as unit. If you want ask for other approaches then make it clear in the question and provide more specific context details based on which we can help you.
    – Fabio
    Jul 4, 2018 at 21:59
  • -1, and voting to close as unclear (it is quite unclear if you mean "TDD just for the BL" or "TDD for the other parts as well"). Moreover, you did not answer my questions above. .
    – Doc Brown
    Jul 5, 2018 at 11:26

3 Answers 3


We are only writing unit tests for Business Logic layer and mocking other parts. Is it possible and reasonable to do TDD in this case?

Yes, in fact that is highly recommended over the "unit test everything" attitude.

Remember, business logic is the interesting stuff that makes decisions. Everything else is just boring infrastructure code that wires up connections.

The boring infrastructure is easy to read but hard to test. The point of tests is to make code easy for humans to read. Boring easy to read code doesn't need unit tests.

This is why we don't unit tests GUI's. Instead we move all interesting logic out of the GUI and put it someplace testable.

The name for seperating hard to test code from interesting code is The Humble Object pattern1,2

TDD works best when you follow this pattern. It basically says if your code is interesting and hard to test then separate the interesting stuff from the hard to test stuff. That can be as simple as turning one function into two. One you unit test. One you don't.

Thinking you have to unit test everything in TDD is quick way to convince people that TDD doesn't work. No that's not what it's for.

Keep in mind there are many other kinds of tests besides unit tests. The slow ones are sometimes called integration tests. That's when you test what happens when you connect the things that TDD isolates. Those tests mostly just show that the connections work. These tests are slow. You don't run them often. Just be sure you run them at least once before you publish.

TDD is about the really fast tests. So fast you run them every time you compile. Some people run them on every keystroke.3 So please, don't confuse these two different kinds of tests and blame the resulting nightmare on TDD. That's not its fault.

Use TDD on what it's for: the interesting stuff.

  • I almost agree. But when you write about separating code to make it testable - this is not TDD! TDD is about writing test code before program code to avoid such situations with bad/no separation (although it's not the only reason to do TDD).
    – Andy
    Jul 5, 2018 at 12:15
  • @Andy separating code doesn't necessarily mean it ever existed before the separation. But even if it does exist already that doesn't mean you can't refactor to testable code. It's just a huge pain, less safe, and has a much smaller payoff. Consider reading Working Effectively with Legacy Code. Jul 5, 2018 at 15:19
  • tbh I think the whole point of TDD is to stop you having to make value judgements about whats worth testing. Test everything! GUI, database, BL. dont write code untill you have a failing test
    – Ewan
    Jul 5, 2018 at 15:23
  • @Ewan I make that value judgement all the time. It's mostly based on speed. Slow tests need their own pile. I want fast tests I can run extremely frequently in their own pile. Slow tests get run before I show my code to others. Jul 5, 2018 at 15:25
  • 1
    @Ewan the only question is whether it is "fit for purpose". I'm not a religious zealot that will faithfully apply TDD to everything under the sun. I am a hard core code monkey that will gleefully spend hours practicing TDD at home finding every little way it can be applied effectively. I will then casually show it off at work to a few fellow coders and see who can use it without it going horribly wrong by being misapplied and misunderstood. I will use it only where I can ensure it's success. I will not use it as a silver bullet. I'm not hunting werewolves. I have real problems to solve. Jul 5, 2018 at 15:41

Strictly speaking no.

TDD requires that everything you write has a test.

However, we all know that some tests are more useful that others. If you have complicated business rules and tests that validate your business rules match the spec then those are pretty useful tests.

If I come along as a new dev and break the database reading code then I probably can see its broken and know how to fix it. It would be nice to have a test but not essential.

If I break an obscure business rule edge case that was designed months or years ago, then I might not even notice anything is wrong without those unit tests.

  • TDD requires to start with a test and keep the test code / program code cycle. It does not require a test for every piece of code you write (which rather indicates for coverage, what was not the original intention).
    – Andy
    Jul 5, 2018 at 12:38
  • If you always start with a test, all your code will have tests. Maybe you dont cover all the paths, but you dont skip whole layers
    – Ewan
    Jul 5, 2018 at 15:19
  • Did you notice OP did not say "is it reasonable to do TDD for all layers" (nor "is it reasonable to do TDD for the BL", they actually refused to add any clarification what they meant). If I got you right, you took the first interpretation of the question, is that correct?
    – Doc Brown
    Jul 5, 2018 at 19:24
  • my interpretation is "if we only write tests for the BL: can anything we do test wise (is it possible) be correctly called TDD" I have a feeling the OP means something other than TDD when they say "TDD" perhaps they mean, "is DIing mocks not proper testing?" or "Is only doing tests for the BL a valid approach?" but those are kind of vague wishy washy questions with only feel good "whatever you do is fine!" style answers. I prefer the more brutal, direct, more accurate, no nonsense, less popular answer.
    – Ewan
    Jul 5, 2018 at 20:05
  • TDD is a programming technique. Can it be applied to individual parts of a system like the business layer, whilst other parts are developed differently? IMHO yes, absolutely. So what is your issue with this?
    – Doc Brown
    Jul 6, 2018 at 14:56

Indeed, it's a very valid approach, as business complexity should be laying on the domain layer. Here, IMO TDD shines here as you don't have to do a lot of ceremony as you typically work with simple types - domain models , value objects, etc - and you can center yourself in modelling policies and behaviors.

I am personally not a big advocate of TDD'ing from the service layer. Here we should be having mostly orchestration code that calls to the domain and I/O adapters. The degree and quality of the patterns and abstractions used here might vary wildly between developers, and TDD'ing here means a certain degree of coupling to the implementation - you assume a sets of abstractions to be injected and knowledge of the services - which ossifies the code being tested and discourages its refactor further down the road. On the other hand, I can see TDD working from the controller, so you are only coupled to your application's external world contracts.

It's a very valid option though to do TDD from the service layer or controller itself on CRUDish apps, setting up a real database. Sure, test executions will be much slower but on this breed of apps you're typically writing a database wrapper so you cannot just assume certain behaviors of such a critical piece of your system.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.