I occasionally do this, though it’s not my first choice (more on that below). When I opt for this approach it’s because what you said (not compromising prod with some test-only backdoors), and also to communicate to the reader that this particular backdoor is required only for tests.
However, in this particular case I’d do something along the lines of
conn = get(authenticate(login, pass), path_under_test)
# assert
The authors mention this option, but they also reason that this would
quickly become expensive, because most tests will require a logged-in user.
This statement feels like an early generalization. With rounds
set to 1 as suggested in pbkdf2 docs, and Ecto sandbox used, the overhead of this step shouldn’t be too large, at least not in smaller projects. Thus, I’d start with authentication via request, and move to some other version if this approach is proven to introduce significant overhead. The change should boil down to changing the implementation of the authenticate
function, so it should be straightforward and localized.
The benefit of actually making a request is that the tests are using the public API, instead of introducing some backdoors. The tests are IMO more straightforward to understand, and they work closer to the way the system is used in production.
A few months ago I started mentoring team in one digital agency, and we introduced this approach to testing. Our general strategy is to use context functions in the arrange phase of the AAA pattern. Developers extract arrange part into private helper function(s) in the same case module. This extraction is first and foremost done to improve the clarity of tests. We aim to keep the arrange phase short and intention revealing without being bothered about mechanical details. If, over time, we need some functions in multiple test cases, we move them into a test/support helper module.
This approach has so far worked well for us, and the team was able to implement a couple of smaller projects without needing to resort to test-only modifications of the lib code. As a result, both the test and the lib are clearer and straight to the point.
If in the future we see that some arrange helpers are causing our tests to be slow, we’ll consider optimizations and we might introduce backdoors where needed, but we’ll do this when there’s actual need, and we’ll do it on a case by case basis, which should keep as much of our tests as possible based on external API.