Dealing with tests for projects that do a lot of side-effects

Haha. It’s funny you mention that. While that outline I started with is very simplified, I first learned this technique from a tutorial about the IO monad in Haskell (:grin: yes, I used the “m” word). My core takeaway was to separate the pure logic that can be easily tested from the side-effects that had to be wrapped in a monad. It’s about separation of deciding what to do from actually doing it. I’ve found it a useful distinction.

Fascinating. I find LSP a “nice to have” and have never let it affect how I design my code. Perhaps that reveals too much about me though :grin: Still, it is important to recognize it has tradeoffs, like all design decisions do. The case expression is a nice mitigation of your concern.

The part about core_complex_logic that bugs me most is that I’m making a function public that could be private purely for testing purposes. Sometimes I live with that tradeoff, but if it became a concern I could move the core decision logic into a new module with @moduledoc false (which isn’t perfect either).

This could be taken even further. Rather than returning a two-tuple, return some sort of a “command” struct inspired by examples such as Ecto.Changeset or Req.Request plus a module that knows how to “execute” the “command”.

4 Likes

Yes, always. That’s what those who abuse DI and all other buzzwords keep missing; they are just tools and should be used sparingly because those in particular reduce a lot of code readability.

I don’t use those features extensively BUT on the rare occasion I want to go 5 layers deep into function definitions and have proper autocompletes and read docs right inside my editor, LSP has been absolutely indispensable. It’s IMO not about getting narcotics-level dependent on it, it’s about allowing it to empower you and save you time and brain cycles, allowing you to focus on your end task, not on 100% of the logistics.

I find the private / public visibility distinction overvalued. Testability > almost everything else, in my book. In Elixir we have boundary and with the tools I keep shilling for (especially ast-grep) you can write your own linter and integrate it in into a pre-commit GIT hook, CI/CD and whatever else you like.

Mind you, I still use defp and use it as much as I can get away with but the moment I feel that I have to test my stuff even more granularly, I don’t get a crisis of faith, I just replace the definition with def and sleep very well that night.

1 Like