It seems the general consensus for unit testing classes is to test your object through its public interface only. So if you wanted to test the
removeElement method on a LinkedList class you'd need to call
removeElement, and lastly
containsElement to assert the element was removed.
This is brittle because if
containsElement broke, then the test will fail even if the implementation of
removeElement is correct.
When I test standalone procedures I try to call them in isolation. If I were to test a
removeElement procedure I would build up the state of the parameters directly in the test and then assert their state post call is correct.
The only difference between a method and a procedure is that a method is implicitly given the object as a parameter. So since
removeElement(list, el) are functionally the same, why not test them the same way? e.g. In the test, create an instance of the LinkedList class, setup up its "private" fields, call
removeElement, and assert its fields post-call changed correctly.
This is the ideal unit test because its about taking input and asserting output for a single unit of functionality. Having to call public methods A, B, C, D, E, and F just to test method G is a borderline integration test, can potentially create false positives (since the data itself is never validated), and makes isolating the failure of the test during maintenance more difficult.
Anecdotally I've found that black box testing tempts developers to add unnecessary public methods to make their testing "easier" but increases maintenance in the long run.
So my question is why is white box testing discouraged in the OO world when it seems like common sense in the procedural and functional worlds?
EDIT: Is there an OO way of dealing with the grips I've outlined in my post, specifically in the later half that do not involve adding new public methods and avoid calling public methods other than the one being tested? Consider the dilemma of asserting the "previous" node pointer in my comment.
EDIT #2: Apparently my concept of a class might be different from others. A class to me is just a (very old) design pattern: "construct", "consume" (e.g. call methods), and "destruct" which is no different than
fread, fwrite fseek, and
fclose in C. Regardless of whether there is an implicit parameter involved, things are stuffed behind a namespace, or you call it private, public, or protected everything is just data and data transformations at the end of the day. I'm having trouble grasping classes as a unit when it seems more like a design pattern or even "container" for the actual units which are the functions themselves.