Not exposing internals hurts testability

I like it, that is a really smart idea - turning the internal function tests into part of the overall tests for the exposed functions.

I also like your approach because it sits well with how TDD is intended to be done. You write tests in order to help you develop the code, and not as an after thought to test code you have already written. I think that is the thing about TDD that a lot of people miss - they think its primary purpose is to test code, when in fact its primary purpose is to guide the development of code through testing. A side effect is that you end up with testable and well tested code.

3 Likes

I think he/she wants to test the smallest testable unit possible (which are functions). Therefore testing the Modules public functions is indeed more like an integration test, since you are not testing the Module as a unit (you can’t), but its exposed functions (those are the units in this case - and we are back to the smallest testable unit again). Testing all the Modules units is the point I think.

1 Like

I think this is a good idea by itself, but nor for beginners or even experienced folks (it is not straightforward and not easy to understand). Also, you will loose your internal tests if I understand this correctly. What if you want to refactor those internal functionalities in the future, with the aid of your tests (which are gone at this point) ? I think one big plus of Elm is, it’s ease of refactor. However, even there you can make mistakes. semantically.

I just think on one hand Elm makes refactoring easy and reliable, but on the other hand, it shouldn’t forget that testing all the small units is also important…

1 Like

The internal tests will still be there, just evolved to be sub-parts of the tests for the exposed API. So you can either refactor those internal test functions with the code, or if the refactoring is more major, I would switch back to exposing (..) and build up some new internal tests functions and then re-incorporate them into the public API tests as you go.

Hm to be frank, that sounds complicated, complex and prone to bugs.

I lean towards only testing what’s exposed. If something is tricky enough that it needs a test, and it’s not exposed, it tends to fit better in a different module that exposes it. Contrived example: if I have some tricky logic that includes some date calculation, and I want to test the actual date calculation too, then I extract it to e.g. a DateCalculations module.

I’ve done TDD across many languages and I can’t think of a feature that Elm’s lacking in this area. As other’s have mentioned TDD forces you to decide where your boundaries are and what behaviour to expose. If you find that there’s an aspect of your internal implementation that you wish to test in more detail it may be a sign that you should extract that behaviour into a separate more focused module and write tests against that.

6 Likes

What if the only exposed function is view and variants of that? That means you will most likely have to test for some outcome of Html msg. (Does elm-test even do that out of the box?) I have a feeling there are a lot of opinions here that are based on a certain use case. Is it a package? An app? Since this question/concern is reappearing throughout the years perhaps there are stuff that causes friction for users. Dismissing it by saying this is fine because I have never had a problem with it might not contribute a lot to a solution…

Not intended to be hostile in any way. Just trying to expand the discussion and hopefully expose solutions to the problem… :sunglasses:

You said “extract into separate module” and that is basically what I wish to do but I don’t want my users to use that module since that is implementation details. But I still want to test it… Am I missing something here?

2 Likes

I would argue that you are sticking to a technique that doesn’t make much sense for Elm, just because other languages use it.

TDD was formulated around the idea that your tests should test everything at almost every level of writing code, both unit and integration tests, and hence reduce the amount of bugs.

Although it sounds sexy it is hard work, a lot of before-hand thinking making sure that you can move trough pastures of green check marks. But if you take a step back, I believe that valid question is how much TDD is worth in Elm? I would argue that testing everything doesn’t make sense in this ecosystem, and it is not worth the effort, i.e. you are not getting you bang for a buck.

Having said that, you still need to test stuff, especially when you are developing parsers, and such data wranglers, you would like to make sure that they work against squad of test cases.

But does it make sense to hide types needed to test then? Especially if you are developing an application, hence exposing some gory details of your package won’t hurt anybody, hence there are nobody using that module except application developers i.e. you.

So, my two cents are:
Relax, compiler does much of the work, and you write a lot to satisfy that guy, there is no need of inventing new hoops that you need to jump trough, just so you can’t see implementation details of your own module. :slight_smile: Hence expose what ever you want to test, it might not be sexy, but at the end of the day, working code is much nicer than theoretically perfect module structure. I have wrote hundreds of thousands of lines of Elm, deleted many many thousands of those lines, because of unnecessary bureaucracy that I invented for my self, because that is what I would do in [obj :C]

Keep it simple <3

2 Likes

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.