Fl4m3Ph03n1x
Background
I have an umbrella project, where I run mix test from the root.
In one of the apps, I am mocking the File module using the Mock library.
Problem
The issue here is that when I run mix test the process dies, with no error message to show:
Manager.Impl.Store.ReaderTest [test/unit/store/reader_test.exs]
* test list_syndicates/1 returns the list of all known syndicates [L#496]** (EXIT from #PID<0.98.0>) killed
Code
The code of the test is as follows:
defmodule Manager.Impl.Store.ReaderTest do
use ExUnit.Case, async: false
alias Manager.Impl.Store.Reader
import Mock
setup do
%{
file_io: File,
paths: [syndicates: ["syndicates.json"]]
}
end
describe "lists syndicates" do
test_with_mock "returns the list of all known syndicates", %{paths: paths} = deps, File, [],
read: fn _filename -> {:ok, "[\"utc\"]"} end do
# Act
actual = Reader.list_syndicates(deps)
expected = {:ok, [%Syndicate{name: "UTC", id: :utc, catalog: []}]}
expected_path = Path.join(paths[:syndicates])
# Assert
assert actual == expected
assert_called(File.read(expected_path))
end
end
end
In comparison, the follow test (which does not use mock) works just fine:
defmodule Manager.Impl.Store.ReaderTest do
@moduledoc false
use ExUnit.Case, async: false
alias Manager.Impl.Store.Reader
import Mock
setup do
%{
paths: [syndicates: ["syndicates.json"]]
}
end
describe "list_syndicates/1" do
defmodule FileMockListSyndicates do
@moduledoc false
def read(path) do
assert path == "syndicates.json"
{:ok, "[\"utc\"]"}
end
end
setup do
%{
file_io: Manager.Impl.Store.ReaderTest.FileMockListSyndicates
}
end
test "returns the list of all known syndicates",
%{paths: paths} = deps do
# Act
actual = FileSystem.list_syndicates(deps)
expected = {:ok, [%Syndicate{name: "UTC", id: :utc, catalog: []}]}
# Assert
assert actual == expected
end
end
end
To me this is rather surprising. One alternative crashes the process with no error message, while the other makes everything work.
To me, this indicates one of three problems:
- A problem with the library Mock
- A problem with my setup of the library
- A problem with the test that causes the process to crash
I believe the second and third options to be the most probable, but without any information about the error, I can’t be sure. The process simply dies.
Question
Why is my process dying, and how can I fix it?
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
Hello,
I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
Hi everyone,
I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding.
I sta...
New
Documentation
While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted”
Version...
New
Other Trending Topics
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Hi everyone!
The first release candidate for the Expert language server project is now available!
We’ve published a press release detai...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #security










Showing Posts 1 to 9- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
D4no0
We ended up refactoring from Mock library to Mox on my last project, even though I’ve heard there is even a better abstracted library built on top of Mox.
From what I understand Mock is using some kind of runtime infrastructure (maybe genservers) to replace and validate the calls, which not only results in inability to have async tests, but also can result in timeouts, the timeouts most probably coming from those underlying genservers.
Fl4m3Ph03n1x
The main issue I have with Mox is that it forces me to define behaviours for everything. Consider my use case, where you need to Mock
Filebecause you don’t want to read/write into the real file system.According to my understanding, I would have to create a boilerplate behaviour for File, and then use it from there. This just adds more maintenance cost with no benefit, only for the sake of using a library.
You are probably talking about Hammox.
I was afraid of this too. Perhaps it is a good idea to post this in the GitHub issues page of Mock as well.
LostKobrakai
Mockdepends on:meck, which does swap out the complete module within the runtime – as in make the VM unload the existing module and load the module with the mock code. That architecture cannot support concurrent tests. The best improvement to get would be better errors or disclaimers.What you call an issue is imo a good driver for well rounded mocking.
There’s the guideline of “don’t mock what you don’t own”. You don’t own the API of
File– the core team does. Elixir does well with not doing backwards incompatible changes, but they could always add new return values and such. You might not become aware of those additional return values, so you won’t be testing for those, which might break your code in production while even well setup tests – working on an incomplete assumption of the interface – would suggest everything is fine.Instead you want to create your own interface (in the form of a behaviour), around the actual usecases you have for interacting with the filesystem. Let’s call it
MyApp.FileStorage. Then you own the interface between your code and the underlying implementation usingFile’s API (MyApp.FileStorage.LocalFiles), as well as the implementation you use in tests (MyApp.FileStorage.Mock).Changes in
File’s APIs then no longer affect your mocked interfaceMyApp.FileStorage. They only affect the implementationMyApp.FileStorage.LocalFiles, which you hopefully tested separately without mocks to ensure it works correctly. Those tests hopefully fail before you push to production. All tests using the mock implementation would be unaffected.One sideeffect of that approach is also that your interface might becomes smaller. Instead of the whole
FileAPI you’ll likely shrink the mocked interface to a few more select things your codebase actually needs, potentially even shrinking the number of possible parameters and return values as well. Complex tasks, which require multiple calls toFileAPI might become a single callback on your behaviour, again simplifying the interface and how much work it would be to mock.Fl4m3Ph03n1x
That is fine by me. I don’t need things to run in parallel or to be faster. At this point, I just need them to work. Disclaimers and error messages would be of great help here.
Let’s assume for the sake of this conversation, that the module in question is indeed
MyApp.FileStorage.I am testing that module. Now the argument I get from people with this point of view usually is:
And that is fine, if you can do it. Imagine however that
Fileactually makes HTTP requests, or that you have to pay for each call, or that you have a limited number of calls you can do, etc … What do you do now?In the article Jose wrote about Mox (Mocks and explicit contracts - Dashbit Blog) he recommends a dummy to solve this problem, which is basically mocking but more complex (he suggests Bypass library for this purpose).
Please do note that I am not stating that your opinion is invalid. I very much agree with your opinion to a certain extent. However, in this specific use case, I don’t think it applies.
Yes, I incur in the typical issue of having tests passing and an application not working. But given that I cannot use the real thing, I genuinely think there is no way around the problem.
So my focus is on “How can I get this working”.
I also want to state I very much appreciate your contributions to my topics, so please do not feel discouraged if sometimes I deviate from them. They still add a lot of value.
LostKobrakai
So you cannot use automated tests – that’s a real and valid limitation. But even if you cannot have that specific benefit of testing the production implementation (which you couldn’t test anyways) you still get all the benefits of having the mocked interface not mapping to that external http API.
The only thing changes in that http api can break is your implementation of your own interface with said http API. It however cannot change your interface itself and to a certain degree your mock can get away not being affected as well.
There’s a few ways your external http api can change:
Actual real life changes might be a combination of those cases.
Having you own interface here is therefore an effective means of limiting the effects external changes can have onto your system. That’s essentially the idea around anti-corruption layers you might be reading about in hexagonal or onion architecture, though they usually explain that in regards to data not computation.
Fl4m3Ph03n1x
Here is, I believe, where our opinions diverge.
I can use automated tests, with the caveat that they will not be reliable 100% of the time.
I’d rather have tests that warn me of something wrong in my code 90% of the time than have nothing at all.
This is not perfect and opens a pathway to false positives, (test passing while application not working in reality because external API changed) as you correctly pointed out.
However, even though I am familiar with API changes and how painful they are to deal with, I find it that in the projects I am currently involved with, the external APIs used don’t change at such a rate that I would rather discard them altogether. They do change every now and then. But it is not so often that I would rather not incur the risk of false positives.
Your experience may differ, you may find yourself in a position of constant and unmitigated change. In that scenario, I would also likely adopt your strategy.
This is also why I am trying to find a fix for this issue using Mock. I find the external API I am using (here represented by
File) to be stable enough.An excellent summary that I agree with!
Will surely like this post, I can see it having a lot of use for future discussions!
Quite an interesting take on a concept I am most fond of !
LostKobrakai
I certainly have made my point and I’ll stop adding comments in regards to that going forward. Though I’d argue that my points will aid in having more code under control, and therefore being able to be tested as much as possible, rather than less. The gaps will be exactly the impossible to cover gaps one has with any approach. So I think we do align on the goal.
Fl4m3Ph03n1x
At the time of this writing, I have tested all major mocking libraries (Mock, Mox and Mimic).
Both Mock and Mimic rely on
:meckunderneath. Rather surprisingly, I get the exact same issue with Mimic as I got with Mock, i.e., the process crashes without warning.This leaves me to the only logical conclusion, which is that the problem I am facing is related to the underlying system that servers as a base for both libraries:
:meck. This issue does not happen using Mox.I have therefore decided not to use Mock (nor Mimic) for the application and I am instead injecting the dependencies directly into the functions that need them. This last approach is very lightweight and does allow for
async: truewhich gives a noticeable speed increase when runningmix test.Because I am only using a very small portion of the external system’s API (2 - 3 functions) this fits well with my needs. However, If I were to use all functions from said API (let’s say 20) this solution would be rather difficult to manage.
I don’t expect the affected modules to evolve in such a direction, so for the time being, I am rather happy.
Here is a sample test for those searching for inspiration (using
Fileas an example):Nezteb
I don’t think Mimic depends on
:meckanymore: Code search results · GitHub