Check "proxy"; good TDD or code smell?

When I write tests for certain types of objects, such as user interface elements such as Forms or UserControls, I often find that I am changing my TDD template; instead of passing the test first, I define and expose the form controls by providing a "skeleton", and then I start writing behavioral tests (data binding / decoupling, display mode behavior, etc.). However, I encounter non-public members. I also face the same problem with other behavioral methods; I can focus on logic and implement logic in some private helper called by another method before worrying about using another method and its behavior.

For me, making everything public (and sometimes virtual) just to be able to test everything is a smell; I do not want other objects to be able to call a helper or directly access a text field; but I need to know that the helper is doing its job, and the text field gets its value when the form loads.

The solution I came to a while ago is to create a "test proxy" for the current test object. The proxy flows from the test object and does not hide or cancel any behavior, but it provides internally visible getters, setters and / or methods that cause calls to non-public members of the tested object, which allows me to inform the object about the execution of certain actions, of which I can view the results without requiring the test to also depend on proper integration within the object or creating a method or some other interesting user in the production code for testing purposes only.

The benefits that I see:

  • Participants' visibility is not determined by whether you want to unit test or not.
  • Finer control over what you can do with the object in the test allows for more flexible and extensible checks.

The disadvantages that I see are:

  • The number of classes is increasing, and an additional level is being developed for testing purposes only.
  • Care should be taken not to use the test proxy in production code in some way (which makes the constructor or the whole class internal, as a rule, does the trick)
  • Not a โ€œcleanโ€ unit test, like you, at some level, depends on the integration between the proxy server and the actual test object.

The question is, is this a valid way of architecting unit tests, or does the fact that I have to do this indicate a problem with the code or testing strategy?

+4
source share
3 answers

My first reaction to this pattern is that you de-emphasize "D in TDD". Your code is validated, but these tests do not control your design, so the code you ended up in has a different structure than if you wrote the tests first. A structure with a more inaccessible private state than necessary. In general, I will argue that if you cannot test the behavior of your class using public interfaces, then you are either writing a test that does not make sense (testing implementation details), or you have a poorly designed public interface.

However, if you work with view classes, this becomes a little more complicated, since you have โ€œpublicโ€ inputs and outputs through your view, which you want to test, but which are not necessarily exposed to code using this view component. In this case, I think it makes sense to give your tests access to this user interface; either by exposing those usually private test attributes (your proxy server is one of the options, and others may be available depending on the language you use) or by writing some kind of functional test that can control the user interface (again the available tools depend on your environment).

+2
source

I would say that this indicates a problem with your testing strategy or code. The reason is because you break encapsulation, which will combine your tests with an implementation, not an interface. This will add to the overall work you are doing, because refactors (for example) may no longer be free.

This suggests that there are good reasons to break encapsulation, and they revolve around functions with side effects (as is often the case with user interface programming). In many cases, you need to assure that functions are called in a specific order, or that they are generally called. I think there are ways to reduce how much you break encapsulation.

If I write tests for side effects, I usually separate them for my test. I will also close / ridicule the side-effect functions and state that they are called according to my requirements (i.e., Order, time, caused or not, arguments, etc.). This frees me from knowing the implementation details, but still allows me to claim that certain functions were called properly.

In some languages, it can be difficult to mock objects or methods. In these cases, I will use dependency injection to convey an object or function with side effects. Thus, during testing, I can pass my layouts for verification.

+1
source

MSTest uses this method to test private methods. Visual Studio puts all the tests in a separate test project and creates the "Accessor" class. This class is a subclass where all private members become public. Since this subclass is in the test project, it is not available in the test assembly. I think this is a viable template for testing private methods and can be implemented manually in the TDD environment if you are not using Visual Studio and MSTest.

+1
source

Source: https://habr.com/ru/post/1334408/


All Articles