15

To what extent do you unit test internal/private components of a class/module/package/etc? Do you test them at all or do you just test the interface to the outside world? An example of these internal is private methods.

As an example, imagine a recursive descent parser, which has several internal procedures (functions/methods) called from one central procedure. The only interface to the outside world is the central procedure, which takes a string and returns the parsed information. The other procedures parse different parts of the string, and they are called either from the central procedure or other procedures.

Naturally, you should test the external interface by calling it with sample strings and comparing it with hand-parsed output. But what about the other procedures? Would you test them individually to check that they parse their substrings correctly?

I can think of a few arguments:

Pros:

  1. More testing is always better, and this can help increase code coverage
  2. Some internal components might be hard to give specific inputs (edge cases for example) by giving input to the external interface
  3. Clearer testing. If an internal component has a (fixed) bug, a test case for that component makes it clear that the bug was in that specific component

Cons:

  1. Refactoring becomes too painful and time-consuming. To change anything, you need to rewrite the unit tests, even if the users of the external interface are not affected
  2. Some languages and testing frameworks don't allow it

What are your opinions?

1

4 Answers 4

9

Case: a "module" (in a broad sence, i.e. something having a public interface and possibly also some private inner parts) has some complicated / involved logic inside it. Testing just the module interface will be sort of an integration testing with relation to the module's inner structure, and thus in case a error is found such testing will not localize the exact inner part / component that is responsible for the failure.

Solution: turn the complicated inner parts into modules themselves, unit-test them (and repeat these steps for them if they are too complicated themselves) and import into your original module. Now you have just a set of modules simple enough to uniit-test (both check that behavior is correct and fix errors) easily, and that's all.

Note:

  • there will be no need to change anything in tests of the module's (former) "sub-modules" when changing the module's contract, unless the "sub-module"'s no more offer services sufficient to fulfil the new/changed contract.

  • nothing will be needlessly made public i.e. the module's contract will be kept and the encapsulation maintained.

[Update]

To test some smart internal logic in cases when it is difficult to put object's inner parts (I mean members not the privately imported modules / packages) into appropriate state with just feeding it inputs via the object's public interface:

  • just have some testing code with friend (in C++ terms) or package (Java) access to the innards actually setting the state from inside and testing the behavior as you'd like.

    • this will not break the encapsulation again while providing easy direct access to the internals for testing purposes -- just run the tests as a "black box" and compile them out in release builds.
3
  • and the list layout seems to be broken a bit ;(
    – mlvljr
    Commented Nov 5, 2010 at 10:14
  • 1
    Good answer. In .NET you can use the [assembly: InternalsVisibleTo("MyUnitTestAssembly")] attribute in your AssemblyInfo.cs to test internals. It feels like cheating though.
    – Nobody
    Commented Nov 5, 2010 at 11:27
  • @rmx Once something satisfies all the necessary criteria it ain't no cheating even if has something in common with actual cheats. But the topic of inter- / intra- module access is really a bit contrived in the modern mainstream languages.
    – mlvljr
    Commented Nov 5, 2010 at 11:45
2

The approach to FSM-based code is a bit different from that used traditionally. It is very similar to what's described here for hardware testing (which is typically also an FSM).

In short, you create a test input sequence (or a set of test input sequences) which should not only produce a certain output, but also when producing a particular "bad" output lets identifying the failed component by the nature of failure. The approach is quite scalable, the more time you spend on the test design the better the test will be.

This kind of testing is closer to what is called "functional tests" but it eliminates the need to change tests every time you slightly touch an implementation.

2

Well - it depends :-). If you're following a BDD (Behaviour Driven Development) or a ATDD (Acceptance Test Driven Development) approach then testing the public interface is fine (as long as you test it exhaustively with varying inputs. The underlying implementation e.g. private methods isn't actually important.

However say you want part of that algorithm to execute within a certain time frame or along a certain bigO curve (e.g. nlogn) then yes testing the individual parts matter. Some would call this more of a traditional TDD/Unit Test approach.

As with everything, YMMV

1

Split it into multiple parts with a functional meaning, eg ParseQuotedString(), ParseExpression(), ParseStatement(), ParseFile() and make them all public. How likely is it that that your syntax changes so much that these become irrelevant?

1
  • 2
    this approach easily leads to weaker encapsulation and larger and harder to use/understand interfaces.
    – sara
    Commented May 7, 2016 at 15:53

Your Answer

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

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