1

In a server application, I have a class called Controller which receive the incoming request. I also have a class called Service which do business logic and a class called Database. All classes are "public", but only one of them is really reachable from the "outside", the Controller.

I am in a dilemma, should I only test the Controller since it is the only callable class or should I test all 3 classes separately?

Pros for testing only the Controller:

  • Dead code easy to find, since it will not be covered
  • Refactor won't break the tests, the Service class is only an implementation detail
  • Some languages have the "internal" keyword that suggest that the class should be consider as an implementation detail, thus to be treated as private
  • Tests won't overlap on each other

Cons:

  • Harder to know how a method in Service and Database are tested
  • It is the same an integration test
  • Setup is harder since the Controller depend everything. By harder I mean that I will have to create a Database which will be injected into the Service which will be injected into the Controller vs only mocking the Service which will be injected to the Controller.
6
  • 1
    Can you precisely define what an internal class is, according to you? You seem to define both Service and Database classes as internal, while some developers would simply call them a class. Jul 27, 2021 at 15:46
  • 1
    It is a c#. java concept where the class is only visible inside the same assembly/package, thus not publicly accessible.
    – Olivier D
    Jul 27, 2021 at 18:03
  • 1
    The crux of your question is whether your internal classes are or are not implementation details, which is ambiguous from your question. You claim they are, but in most contexts I can think of, I would believe that neither a Service nor a Database class is an implementation detail of a Controller class, merely that they are dependencies which I would assume are themselves tested in isolation. I think you should either revise your example or your usage of internal class in order to clarify this ambiguity. Jul 27, 2021 at 18:14
  • You are right sir! You made me realize that injected classes must be public, thus must be tested separately. I took my example from a python code, and I wrongly assumed that they were internal classes.
    – Olivier D
    Jul 27, 2021 at 18:28
  • Re, "Harder to know how a method in ['internal' classes] are tested" Sometimes, that just means that you did not design those classes with testing in mind. If you embrace test-driven development then you won't often run into that problem. Jul 27, 2021 at 18:57

3 Answers 3

2

Any class that is marked public (or internal or package private in some languages, which just means it's only publicly accessible in the same assembly or package) is eligible for unit testing, except for those classes stated below.

A test that tests a public class indirectly (i.e. through some API endpoint like a REST interface) is an integration test, not a unit test.

You don't have to test private methods. Private methods can be tested indirectly using the class's public methods.

Here are the classes you should not unit test:

  1. Nested classes; that is, classes within classes.
  2. Classes with no behavior, like Data Transfer Objects.
  3. Anonymous classes.

Setup is harder, since the controller...

Then refactor the class so that it is easier to test. One reason unit tests are valuable is that they point out deficiencies in your designs.

11
  • "The metric is not "it is directly accessible to the outside world," it is that the class is a "unit," or more accurately, its methods are units. Hence the name "unit test."". Completely disagree. The first metric to use when deciding whether to write an automated test for a particular piece of code is exactly is it directly accessible to the outside world. If it isn't then it's an implementation detail and should not be directly tested. The word "unit" is just an excuse for pointless semantics.
    – David Arno
    Jul 27, 2021 at 15:33
  • Then define what you mean by "outside world." My definition of "outside world" is that the class is marked public. The OP's definition of "outside world" is some sort of API endpoint like a REST interface. My answer already states that you don't have to unit test private methods, and the rest of my answer goes into some detail about what does and does not need to be unit tested. Jul 27, 2021 at 16:51
  • In any case, I've removed the "objectionable" content, since it seems to be confusing an otherwise straightforward concept. Jul 27, 2021 at 17:04
  • The trouble is, you haven’t removed that objectionable content. Internal classes are implementation details. They should not be directly tested. So your advice that they should be remains bad advice in my view.
    – David Arno
    Jul 27, 2021 at 20:08
  • 1
    ... note I am not saying what you wrote is completely unreasonable, but I think it makes a difference what kind of software one writes and how / for what purpose classes are marked as internal. When it comes to internal classes, there are 3 options of testing: 1. testing them only indrectly, through other public methods. 2. making them public for easier testing, or 3. using InternalsVisibleTo to allow direct testing. All 3 options can be reasonable, depending on the context, and the kind of software one writes, there is no hard-and-fast rule when to pick which approach.
    – Doc Brown
    Jul 28, 2021 at 12:20
2

Vincent Savard made me realize 2 things. That I have a design problem not an internal test problem and a public class is not necessarily API public.

If the Service and the Database are instantiated in the Controller I have a coupling problem.

If the Service and the Database are injected in the Controller, then they are public, thus they can/must be tested.

-1

"Well, there are many different kinds of tests, and ways of running them."

Every class ought to –somewhere – have a "test rig" that thoroughly tests it. Even though that test rig constructs a thoroughly-artificial situation in order to do so. But in the case of "internal" classes you probably need to run these tests only after a change has actually been made to that class.

The very worst thing that can happen to any house is "foundation problems." Therefore, you need to know that the foundation ("inner classes") is solid, before you can meaningfully test anything that's built on top of it. "Okay, I see that this wall isn't quite square, but I know that the problem has nothing to do with the basement – I think that I can eliminate that possibility."

(The ability to "think that you can eliminate that possibility" is huge.)

Build a series of "easily re-runnable test rigs" which can automatically perform the various test sequences that are needed (probably invoking other test-rigs in order to do so). Maybe you don't "routinely run" all of them, but prior to a major release you should run "the test-rig that runs absolutely everything."

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.