bian2510

bian2510

** (Mox.VerificationError) error while verifying mocks for #PID<0.534.0>: * expected DirectHomeApi.Aws.MockS3.upload_files/1 to be invoked once but it was invoked 0 times

Hello guys I’m trying mock a function that is executed when I call my endpoint (In the middle of all code).

But when I call my endpoint all is working but th mock not is called and upload the file to s3. Can someone give me a help with this?

Thanks! :smiley:

This is the error

  1) test upload user image when the user is logged (DirectHomeApiWeb.UserControllerTest)
     test/direct_home_api_web/controllers/user_controller_test.exs:165
     ** (Mox.VerificationError) error while verifying mocks for #PID<0.534.0>:
     
       * expected DirectHomeApi.Aws.MockS3.upload_files/1 to be invoked once but it was invoked 0 times
     stacktrace:
       (mox 1.0.0) lib/mox.ex:692: Mox.verify_mock_or_all!/3
       (ex_unit 1.11.3) lib/ex_unit/on_exit_handler.ex:143: ExUnit.OnExitHandler.exec_callback/1
       (ex_unit 1.11.3) lib/ex_unit/on_exit_handler.ex:129: ExUnit.OnExitHandler.on_exit_runner_loop/0

user_controller_test.ex


defmodule DirectHomeApiWeb.UserControllerTest do
  use DirectHomeApiWeb.ConnCase
  use ExUnit.Case, async: true

  import Mox

  alias DirectHomeApi.Model.User
  alias DirectHomeApi.Repo


  doctest DirectHomeApi.Aws.S3

  setup :verify_on_exit!
  describe "upload user image" do
    test "when the user is logged", %{conn: conn} do
      DirectHomeApi.Aws.MockS3
      |> expect(:upload_files, fn _image -> {:ok, %{}} end)

      image = %Plug.Upload{
        content_type: "image/jpeg",
        filename: "test.jpeg",
        path: "test/images/some_image.jpeg"
      }

      user = create_user()

      conn =
        sigin_and_put_token(conn, user)
        |> post(Routes.user_path(conn, :upload_image), %{"id" => user.id, "photo" => image})
      
      assert 200 = conn.status
      assert {:ok, %{"sucess" => "The image could be saved sucessfully"}} = Jason.decode(conn.resp_body)
      #%{"error" => "The image not could be storage in s3"}
    end
  end
end

s3.ex

defmodule DirectHomeApi.Aws.S3 do
  @callback upload_files(arg :: any) :: {:ok, map()} | {:ok, map()}

  def upload_files(image) do
    content_type = image.content_type
    format_file = String.split(content_type, "/") |> List.last()
    file_name = "#{Ecto.UUID.generate()}.#{format_file}"
    file_path = image.path
    bucket = System.get_env("BUCKET_NAME")

    ExAws.S3.put_object(bucket, file_name, File.read!(file_path), [
      {:content_type, content_type}
    ])
    |> ExAws.request()
    |> case do
      {:ok, _} -> {:ok, file_name}
      {:error, error} -> {:error, error}
    end
  end
end

test_helper.ex

ExUnit.start()
Ecto.Adapters.SQL.Sandbox.mode(DirectHomeApi.Repo, :manual)
Mox.defmock(DirectHomeApi.Aws.MockS3, for: DirectHomeApi.Aws.S3)

Marked As Solved

edisonywh

edisonywh

That’s what yukster pointed out, you are not swapping out the implementation in your user.ex.

The error message is telling you exactly that:

expected DirectHomeApi.Aws.MockS3.upload_files/1 to be invoked once but it was invoked 0 times

In your code you never made a call to MockS3.

You have a line like this:

 def update_image(id, user_image) do
    S3.upload_files(user_image["photo"])
...

You see how it directly calls S3? You need to inject the dependency here, so something like this instead:

 def update_image(id, user_image) do
    s3_provider().upload_files(user_image["photo"])
...
end

def s3_provider, do: Application.fetch_env!(:your_app, :s3_provider)

In your config you would need to have s3_provider be configured to the real S3 omdule, and in your test you would need to configure it to use your MockS3.

This line:

Mox.defmock(DirectHomeApi.Aws.MockS3, for: DirectHomeApi.Aws.S3)

This creates a mock for you based on the behaviour, but you still need to inject the dependency yourself

Also Liked

karlosmid

karlosmid

Mock reported that mocked function was not called.

From this code, it seems that you are calling the wrong controller. Do you have controller with function named upload_image? Try to replace upload_image with upload_files

yukster

yukster

Also you don’t show the controller code… The code that calls S3.upload_files. Are you swapping out the real module for the mock? Defining the mock is only half the solution. You need to also set up test.exs to put the mock in the env and make the controller get the S3 module from the application env instead of using the module directly. Hope that helps!

yukster

yukster

Thanks, @edisonywh! Yep, that’s exactly what I was getting at (but was replying on a tablet in the middle of the night with insomnia, so I didn’t give examples). Thank you for the explicit instructions.

I think the official docs could be better about this: Mox — Mox v1.2.0. It talks about putting the module being mocked into the app config but the example of using a mock doesn’t take that approach.

Basically, there’s no magic involved. Just defining a mock doesn’t make it be used. You have to make the real code use the real module and the test code use the mock.

Their example also uses it more like a stub, just returning a value, not pattern matching the args, and not asserting anything about how the mock was called. If I’m going to use expect then I like to assert something in the function body passed to it. Or at the very least, pattern match the params so that the test will fail if the mock isn’t called the right way.

Anyway, @bian2510 I hope all this helps and doesn’t confuse you even more. I definitely struggled a bit with Mox when I first adopted it. And it still bothers me some that I have to alter the production code in order to use a Mox mock. But that alteration is limited to just making a given module configurable via the application env so it’s worth it.

Last Post!

bian2510

bian2510

Yes guys, this is the solution, thank you very much.. @yukster @edisonywh

Where Next?

Popular in Questions Top

nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New

Other popular topics Top

JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 54921 245
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 49084 226
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New

We're in Beta

About us Mission Statement