Covering all constructors

I’m working on making a Route type which can be used throughout my project to refer to the possible routes in my frontend and convert them to/from URLs. A common sanity check/test for this kind of thing is to try “round-tripping” routes through the to-URL/from-URL functions to ensure everything is covered and consistent. So I wrote a fuzzer for my Route type, which was straightforward.

I expect this type will change as I add pages to my project. When this happens, I will have to remember to update the fuzzer. If I don’t, my test will become invalid because the new cases I added will not be tested in the round trip.

Has anyone come up with a clever way to ensure that the fuzzer stays in sync with the underlying type? One idea I had was to add a dummy reminder function just under the fuzzer which does an exhaustive pattern match on the Route type. Then, if a constructor is added, at least the reminder function will break, and hopefully that will serve as a reminder to update the fuzzer. But maybe there’s another way?

Is this a non-issue for other people, perhaps because you keep your fuzzers closer to the underlying type (as hinted at in e.g. Where to put encoders, decoders and generators?)?

1 Like

I think the established pattern at this point is using a reminder, just like you said.

That is also what we used to do at my workplace. Now, we use a custom elm-review rule instead. By convention, we consider all values that look like allXyz : List Xyz to need to have all the constructors of the type Xyz, which gives us the following error message

-- ELM-REVIEW ERROR ------------------------------------------ src/Route.elm

NoMissingTypeConstructor: `allRoutes` does not contain all the type constructors for `Route`

11| allRoutes : List Route
12| allRoutes =
    ^^^^^^^^^
13|     [ HomePage


We expect `allRoutes` to contain all the type constructors for `Route`.

In this case, you are missing the following constructors:
  - SomePage

The code for it is in this gist. I will probably make a package for it at some point. The limitation is that you can only do this for types that are defined in the same file as the allXyz constant, but that limitating will change soon with an upcoming release of elm-review.

What I regret about this pattern, is that you lose this safety net as soon as someone renames the allXyz constant. But the reminder is brittle in a similar way, so :man_shrugging:

3 Likes

The reminder-function is the only thing that works AFAIK.

There was a proposal:

1 Like

Thanks, this seems like a great use of elm-review.

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