4

I'm currently learning about TDD techniques, one of the suggestion is to test only public methods and skip the private ones. I have also been reading about Mocking. If I want to mock a certain method, then it needs to come from an interface or be marked as virtual. When I start developing my application I don't know which methods I will want to mock while creating unit tests, because of that I think it's best to make them all available for mocking.

I assume that making all methods virtual isn't the best solution, then the alternative is the interface approach mentioned above.

Is that the right direction or do I miss something obvious?

  • 3
    Try write test first, even it will not compile. Just type whole test code. Then step by step make it compile. Idea of TDD is that you will see what should be locked while or after test is written. – Fabio Aug 8 '18 at 6:40
  • @Fabio That doesn't quite answer the question though. If OP is in a situation where he knows there won't be full test coverage, the question remains valid for those methods that won't actually be tested. While it's of course better to have full test coverage, not every manager agrees on that point and in reality not every project will get full test coverage. – Flater Aug 8 '18 at 7:53
  • 1
    @Flater, OP 's question is about TDD. So when you write test before writing production code you don't care about code coverage - you care only about to write enough test cases to be confident that unit under the tests works correctly. You can mock(introduce them as abstraction) all dependencies, you think, will make tests slow or to complicated. Then you can make another decision to test or not implementation of those dependencies. Usually when you write tests first you end up with 100% code coverage. – Fabio Aug 8 '18 at 8:15
  • 2
    Public methods ARE interface. Even if a class does not explicitly implement a programming language interface, it's public methods can be used in a thin adapter to it. I find it useful to think in terms of contracts, not interfaces as programming language construct. Then questions like this become trivial. – Basilevs Aug 8 '18 at 12:32
  • @Basilevs Perhaps you have not been following interface questions in this forum, but interfaces have a magical quality for many people, that can never be explained logically, but that elevate them above the ordinary data type. Logically, to preserve abstraction, the user of an API should not even know if a type is a class or a specialized class (an interface), and there should be no special naming convention for interfaces. – Frank Hileman Aug 14 '18 at 22:44
5
  • Fabio touched on it in his comment - it is perfectly legit to start using a dependency in a test before any interface or implementation code exists for it.

    When you feel that your object under test needs indirect input from somewhere else, or that you need to test indirect outputs of your object, start typing your stubbing or mocking code as if those external dependencies existed. Then use IDE productivity features to generate the interfaces and their method signatures.

    This is called "programming by wishful thinking" and is used a lot in outside-in TDD.

    Your question makes less sense in that context, since you wouldn't create any new interface or any method inside an existing interface without mocking them first.

  • Another context is when you create dependencies by extracting new classes during the Refactor step of the TDD cycle and, in a more "Classicist" style, decide not to test them with mocks.

    Here, the answer to your question Should all class public methods come from an interface? is clearly: "no". You can perfectly have a TDD-written test that tests objects A and B without either of them implementing an interface.

    However, I would use this approach only when the extracted dependency is in the same layer as the object under test, not at application boundaries/seams. Having interfaces at architectural boundaries is often more valuable because it is usually where you get the most fragility, the least performance, the most complex configuration, and the highest likelihood of needing multiple implementations of the same abstraction.

  • Thanks for all additional links! I will dive into the topic. – ArturoO Aug 10 '18 at 18:01
2

Should all class public methods come from an interface?

No -- it's a really interesting constraint to experiment with, but it isn't necessary.

If I want to mock a certain method, then it needs to come from an interface or be marked as virtual.

Mocking a specific method makes me twitchy -- if you are trying to mock the method, rather than the object, it begins to sound like you don't really have the correct grain to your objects.

If I ran into that need, I would look very carefully to see if I really have two objects -- one that is responsible for the bit I want to mock (and nothing else), and another that uses the previous object as a collaborator.

(Part of the point of is to be sensitive to when your design is breaking the rules, because that often indicates a weakness in the design.)

As a rule, we shouldn't need to mock arbitrary public method, so I would not encourage you to optimize for that use case prematurely.

1

I'm currently learning about TDD techniques, one of the suggestion is to test only public methods and skip the private ones

Yes, TDD focuses the efforts on defining the test through the contract first and getting the implementation later. We define first the methods (mostly public), their arguments, their returning types and the exceptions that can be thrown.

These contracts might or might not come from interfaces or virtual methods. They could be mere empty methods of a new class. So, no, they don't all come from an interface. TDD will not force your design in that way. 1

I have also been reading about Mocking

Here is where you are getting lost. Mocks (IMO) are an advanced technique for testing. Not directly involved with TDD. You can still follow TDD with no mocks. Mocks are addressed for testing components with heavy dependencies (as @guillamue31 commented in his answer). it provides a "fake" instance of a given interface. One which behaviour can be programmed in order to meet the conditions required for the use case under test. It could be a class too. Yes, concrete classes can be mocked too.

When I start developing my application I don't know which methods I will want to mock while creating unit tests, because of that I think it's best to make them all available for mocking.

Fine. Let TDD tell which components are good candidates to be mocked. After several iterations of TDD, once tests go green, it's easier to see which components need decoupling and therefore, the candidates to be mocked. But keep in mind that not every single dependency implies an interface. That's up to you to decide based on the requirements, needs and preferences.


1: I would like to share a video of Sandro Mancuso implementing Rovers Mars kata. You will see that he didn't use any interface to test contracts. Mancuso is very in favour of this methodology and he encourages it to all his employees

2: Note that Sandro is following TDD and Rover's methods are protected!!! He is playing with the package scope. Test and concrete class are in the same package. So, TDD doesn't even force us to define methods as public.

  • Why implementation detail is public? – Basilevs Aug 8 '18 at 12:37
  • Where I have said that? – Laiv Aug 8 '18 at 12:38
  • >We abstract ourselves from the "implementation details" – Basilevs Aug 8 '18 at 12:39
  • And where did you see the word public? I was making an example of what we do when we follow TDD. Focus on the contract first rather than on its details – Laiv Aug 8 '18 at 12:40
  • Question asks about public methods – Basilevs Aug 8 '18 at 12:40
0

No. You only need Interfaces for classes which are used as dependencies

for example:

public class Application
{
     private IService service;
     public void Main() {...}
}

public class Service : IService
{
    public Customer GetCustomerForOrder(Order order)
}

public class Customer 
{
    public decimal GetBalance() {...}
}

public class Order 
{
    public decimal GetTotal() {...}
}

Here only Service needs to have an interface. When we test Application we will want to inject a mocked Service and the Interface enables that, plus general replacement with alternative implementations.

Mocking Application doesn't help you at all.

Customer and Order can be tested directly as they have no dependencies.

  • Just to mention that mocking all dependencies will bite you later during refactoring, where adding/removing parameter in interface contracts will force you to make changes in the tests as well. Suggest to mock only dependencies which makes tests slow(database, external resources) or dependencies which require very complicated test case configuration. – Fabio Aug 8 '18 at 9:59
  • You only need Interfaces for classes which are used as dependencies But the inherent nature of public methods is so that others can use them, which inherently creates a dependency. Your answer only focuses on testing the business logic, but it's perfectly possible to test more than just the business logic, at which point you do need multiple interfaces. Secondly, it's possible to use dependency injection regardless of unit testing, which would still warrant using interfaces. – Flater Aug 8 '18 at 10:05
  • @Flater I think its pretty clear which classes are designed to be dependencies and which are not. Obviously you could make the case that anything is potentially a dependency, but I think its possible to make sensible decisions – Ewan Aug 8 '18 at 10:08
  • @Ewan: I'm not saying you're wrong, but your answer might benefit from strictly defining what you mean by "dependency". Because what you say conflicts with the notion that public methods are inherently designed to be used externally and therefore are depended upon by external callers. – Flater Aug 8 '18 at 10:20
  • @Flater urg, I just tried that but it makes the answer unwieldy and just goes off into a long discussion on various types of dependency. Yes, you are correct that for example Order, might be considered a dependency, but I think my example better demonstrates what I mean than a long explaination – Ewan Aug 8 '18 at 10:26
-1

Ideally, you should test all public methods, which does indeed mean that all public methods should be mockable.

Due to confusion in the comments, I want to point out that with "mockable", I mean "able to be tested". In other words, part of the interface definition (or, as per the suggestion in the question, made virtual, though I'm not a fan of this approach over interfaces)

In reality, you won't always test everything. In my current project, for example, I've had to struggle just to get the time to implement unit tests (management didn't consider it necessary). As a compromise, we agreed to only test the business logic that changes the state of the objects, and to not test retrieval methods which only fetch data.

I assume that making all methods virtual isn't the best solution, then the alternative is the interface approach mentioned above.

Is that the right direction or do I miss something obvious?

I do not see a reason to use the virtual approach. Interfaces are already much more commonplace (especially when coupled with dependency injection), and they already cover all the necessary bases.

So I suggest using interfaces instead of virtual methods.

If someone can show me an valid case where virtual methods are better than interfaces, I'm happy to hear it.

Note
Even if you know you're not going to test certain public methods (such as the data retrieval methods in my current project), you should still include these methods in the interface. Just because you're not testing them today doesn't mean you won't test them tomorrow.

  • 1
    This can sounds cliche, but worth to mention: Testing is part of developer's work process, you shouldn't ask permission for writing unit tests, same way as you not asking permission for using polymorphism over switch statements ;) – Fabio Aug 8 '18 at 10:03
  • ..unless you want to keep your job – Ewan Aug 8 '18 at 10:05
  • 1
    @Ewan, if you cannot change company - change company ;) – Fabio Aug 8 '18 at 10:06
  • @Fabio, +1. Use TDD; write tests before you write code. Tests are non-negotiable. If you can't write tests, you can't write code. If your management won't let you do your job properly, "sack them" by leaving and getting a job in a sane company. – David Arno Aug 8 '18 at 10:07
  • @Fabio (and DavidArno): I wish you luck with your "my way or the highway" approach. I have yet to come across a single employer (15+ so far as a consultant) where budgets and deadlines were freely choosable by the developer. I'm all for considering the technical need for testing, but your hardline stances will simply render a developer an undersirable hire as opposed to making the project more workable. – Flater Aug 8 '18 at 10:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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