ntd23

ntd23

Hi again ppl,

I really like the idea of test my private functions, mainly after read this article of @pragdave, but the library that he create is a little old and the article too, the last modification on code was 2 years ago . So, I have two questions:

  • I get much risk using this lib, even it being “stable”?
  • Is really worth test many private functions as I can or the article already get a strong oposit response?

Showing Posts 1 to 10

dimitarvp

dimitarvp

I haven’t reviewed the library but I don’t see why it would be unstable or dangerous to use? It’s a pretty small-scoped library so it might just be considered complete and does not need further changes.

This is a broad question and nobody can answer that for your project except yourself. I personally do my best to extract out all the bits and pieces that my higher-level business functions are using, into smaller utility functions, and usually make them public throughout the project. Doesn’t hurt anything IMO.

So:

  • Have smaller amount of private functions. Abstract away things as long as they make sense as separate functions (utilize your experience and knowledge to make that call; there are no deadly consequences if you get it wrong).
  • Those private functions that end up remaining private are not important enough to try and test in isolation. You will test the behaviour of your system e.g. sending particular combination of parameters to your controller and will then check if certain DB objects have been created / updated / deleted, certain emails have been sent, or message queues filled, notifications sent etc. The private functions are not important there. If the test fails you’ll start to track what went wrong and you’ll fix everything along the way, the private functions included.
mayel

mayel

Have smaller amount of private functions.

I’ve been thinking about this topic after seeing the following tweet thread. I often make more functions public than I would otherwise just to use them in tests (or in fake data generation modules to be used in tests or seeds) which I’d like to avoid…

https://twitter.com/mechanical_monk/status/1581978963399757824

dimitarvp

dimitarvp

I agree with “deep modules good, shallow modules bad” 95% of the time. That philosophy wants the value-add of modules/classes to be “simplify things so they are workable” which I absolutely agree with.

But every now and then you need a module exposed just so they have certain responsibility isolated to them. Example: data-access modules in enterprise settings; there you absolutely don’t want people to just use Ecto.Repo indiscriminately so having stuff like update_user (like in the tweet) does make sense. But I believe we both agree that these cases are rarer and more specific.

Having a not-super-useful module that simply points at itself and says “I am responsible for X in the project” is a value-add as well.

Point is, we must optimize for human readability and comprehensibility above all else. This means sometimes we must make small compromises with otherwise excellent practices – like the one outlined in the tweets.

dorgan

dorgan

I have been using Patch — patch v0.16.0 quite a lot recently. It’s very well documented and behaves as advertised. It’s a powerful alternative to Mox though it can also be used alongside it.

Private functions frequently go untested because they are difficult to test. Developers are faced with a few options when they have a private function.

  1. Don’t test the private function.
  2. Test the private function circuitously by calling some public functions.
  3. Make a public wrapper for the private function and test that.
  4. Change the visibility to public and put a comment with some form of, “This is public just for testing, this function should be treated as though it’s private.”

Patch provides a new mechanism for testing private functions, expose/2.

al2o3cr

al2o3cr

IMO if a function is complicated enough to merit its own test it’s probably complicated enough to merit a @doc - and putting one on a private function emits a warning (see many discussions, like this one for more).

mayel

mayel

That’s a great point @al2o3cr, though I wish there was a more supported way to document functions that you aren’t part of in a module’s public API (especially when writing or reading a library’s code). Basically I’d like to have documented and testable functions without having to worry that users of the library are calling them directly when refactoring.

evadne

evadne

The most inoffensive way would be to write modules that have @moduledoc false or simply name it Internal and tell folks to not use it from the outside, but still document it anyway.

ityonemo

ityonemo

I tend to default to private functions that I don’t bother to test in isolation (I mostly write integration tests). If I really need to test partial functionality, I wrap the private functionality in a public trampoline:

  if @env == :test do
    def _my_private_function (...) do
       private_function(...)
    end
  end

I think this kind of explicit strategy is better because it unclutters your tab completion and unlike strategies to expose functions sneakily it minimizes the amount of surprise when reading code or getting stuck I’m a tight spot.

Side note: getting @env to be usable in an on-the-fly compilation in situations where you don’t have Mix is tricky, usually I save the environment in its own module

sodapopcan

sodapopcan

A big +1 to @evadne’s comment and I’d like to expand my thinking on this.

I try and design so that a child a module should always be considered private unless otherwise stated by documentation. If I have a module that covers a larger surface area, I break it up into smaller modules, test those, and expose those child module’s public functions as defdelegates through their parent. If I have a module that covers a very large surface area then I document the child module as public and that becomes the boundary where nothing but functions are allowed “out”.

This is generally how most libraries work. For example, Phoenix exposes three “levels” of modules. The direct children are the major features and libraries, like Phoenix.Router and Phoenix.LiveView respectively (I’m sorta making up the names “features” and “libraries” for this context). Looking at the docs, all the feature modules only provide functions as their public API even though they are comprised of one-or-more sub-modules that we don’t (well, aren’t supposed to) touch. Phoenix.LiveView is its own thing and drills down one more level where we have access to a bunch of its child modules. This children are also made up of submodules that we don’t (or shouldn’t) touch.

Otherwise, as @dimitarvp points out, nothing is set in stone and there is always room for compromise.

evadne

evadne

If I have functions in modules that only appear during testing then I am not testing the same code that would run in production.

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
itzmidinesh
Hi all, I’m working on a Phoenix project where I need to make external HTTP requests and I’m trying to choose between Req and HTTPoison....
New
pragdave
How do I tell Hologram not to escape HTML when substituting into templates? {Textile.render(@product.long_description)} escapes the HTM...
New
rlipscombe
I’d like to create a file with 600 permissions (rw-------), but there’s a race condition if I create it and then change the permissions. ...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New
rlipscombe
When I use Mix.install with a git dependency, using git@github.com:... how do I change the SSH identity used? I’ve got two Github accoun...
New

Other Trending Topics Top

dominicletz
Hey there, I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first. ...
New
raa-ziq
Hi everyone :waving_hand: I wanted to share something I’ve been building — NowBlind, a random chat app designed to help people discover ...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New
kip
Localize is the next generation localisation library for Elixir. Think of it as ex_cldr version 3.0. The first version will be released ...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews