woohaaha

woohaaha

I’m trying to wrap my head around using mocks in Elixir. There are a lot of blog posts on the subject but I’m not seeing any that address my use cases.

(no need to recommend Mocks and explicit contracts « Plataformatec Blog, read it a few times :wink:)

To be a bit more specific, here are my scenarios/questions:

I would like my integration tests to write-to/read-from the database. I don’t want my unit tests to touch the database. I’d like to use a mock for the persistence layer. How can I do this with Mox? It seems all the examples require one into an all-or-nothing commitment. You’re either using the DB or not.

On dependency injection… I don’t mind injecting a mock Repo in my unit tests but I don’t understand how that scales into intermediary modules when I have to test those. For example:

defmodule MyApp.Accounts do
  def list_users(repo \\ MyApp.Repo) do
    repo.all(User)
  end
end

defmodule MyApp.Dashboard do
  # do i inject MyApp.RepoMock here? i'd need to DI all these functions. how do i test with mocks in this context?
  # def get_the_world(repo \\ MyApp.Repo) <---- ???
  def get_the_world() do
    so_many_users = MyApp.Accounts.list_users(repo)
    MyApp.AnotherContext.more_stuff_to_mock_stub(MyApp.DI1)
    MyApp.Context3.yup_more_goes_here(MyApp.DI2)
     ...
  end
end

Would appreciate it if someone can explain how I can use Mox in unit tests and not in integration tests. Would also like to know how DI works without having to pass modules through multiple layers just to have precise control in my unit tests.

Thank you :nerd_face:

Showing Posts 1 to 10

rodrigues

rodrigues

Hi @woohaaha, in Mox v1.2.0 — Documentation you can see how to set a mock using application env as dependency injection:

Application.put_env(:my_app, :calculator, MyApp.CalcMock)

You can have some tests doing that, while other tests use the non-mocked implementation:

Application.put_env(:my_app, :calculator, MyApp.RealCalculator)

To see how to do this in ex_unit, you can take a look at setup and ExUnit.CaseTemplate.

axelson

axelson

Scenic Core Team

Note: if you use that approach during a test then you need to ensure that test is marked as async: false since the application env is global. But if you only called put_env in your integration tests and marked them as global that could work well.

woohaaha

woohaaha OP

@axelson, indeed i want async: true so I’m not sure Application.put_env(:my_app, :calculator, MyApp.CalcMock) in some tests and not in others will be reliable.

I’m not sure what you mean here. Running the tests as a suite means that setting the environment during integration tests will leave it set for any follow on unit tests, no? Are you suggesting I run integration and unit tests separately? I’m not sure how marking them as global (set_mox_global?) changes those concerns.

woohaaha

woohaaha OP

Thank you @rodrigues for the reply. @axelson had some points related to your answer which mean the tests could be flaky, no?

woohaaha

woohaaha OP

Not sure how to update the original question as the Edit button is missing… but here’s an update.

I would prefer to run the tests concurrently.

I do not need Mox (just thought that was the right tool for the job… but what do i know?).

(I am willing to split my test suite to only running integration tests, and then the unit tests separately… but that would make me sad :cry:)

I have done a lot of reading at this point as there’s plenty of articles out there, but nothing to address my requirements (which I think are realistic). It could also be that maybe I just don’t get it so… can anyone explain it to me like I’m 5? Or, better yet, since this is a topic I’ve noticed some people get heated on, maybe someone could write a blog post or create a youtube video showing exactly how it should be done? (In a real world-ish example, like a Phoenix app with integration tests and unit tests and it all being concurrent)

ityonemo

ityonemo

  1. Use Mox.
  2. For your unit tests, do the normal Mox thing.
  3. For your integration tests, use stub_with and stub out to the unmocked module, this will make a database call.
  4. read up on how to make Ecto tests async.
axelson

axelson

Scenic Core Team

I was suggesting that you could use the Application.put_env approach for all your integration tests and mark them as async: false. But I’d second @ityonemo’s approach of using Mox.stub_with in your integration tests. That will work for most cases as long as you aren’t spawning additional processes.

ityonemo

ityonemo

It also work with all spawned processes linked with the Task module (which is one reason why you should always always always use Task in your code)

woohaaha

woohaaha OP

Thank you for that. I read up about Task (Task — Elixir v1.20.2). I’ll need to find the common use cases for Task so will search for that.

ityonemo

ityonemo

I should be careful. What I mean is “always always use Task instead of a naked spawn or spawn_link”. If you don’t need async (most of the time you don’t), don’t use a Task.

Where Next? Top

Trending in Questions Top

RSP87
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
nseaSeb
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
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
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
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New

Other Trending Topics Top

JesseHerrick
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
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
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
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
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

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews