Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mocking modules with methods generated by macro #13

Closed
ayrat555 opened this issue Oct 6, 2017 · 3 comments
Closed

Mocking modules with methods generated by macro #13

ayrat555 opened this issue Oct 6, 2017 · 3 comments

Comments

@ayrat555
Copy link

ayrat555 commented Oct 6, 2017

Is it possible to mock methods that are generated by macro?

For example, there is some behaviour

defmodule Client.Behaviour do
  @callback request(map()) :: {:ok, map()} | {:error, map()} | {:error, atom()}
end

and there is some macro that implements this behaviour

defmodule Client.Macro do
  defmacro __using__(_) do
    @behaviour Client.Behaviour

    methods = Client.Methods.methods # a lot of methods with format {binary, atom}

    quote location: :keep, bind_quoted: [methods: methods] do
      @behaviour Client.Behaviour

      methods
      |> Enum.each(fn({original_name, formatted_name}) ->
       def unquote(formatted_name)(params) when is_list(params) do
         send_request(unquote(original_name), params)
       end
      end)

      def send_request(method_name, params) when is_list(params) do
        # params preparation

        request(params)
      end
      
      def request(params) do
        {:error, :not_implemented}
      end

      defoverridable [request: 1]
    end
  end
end

and then there are a couple of client that implement only request/1 method

  defmodule Client.HttpClient do
    use Client.Macro

    def request(params)
      # http stuff
    end
  end

 defmodule Client.IpcClient do
    use Client.Macro

    def request(params)
      # Unix socket stuff
    end
  end

this clients are used in other modules so I need somehow mock methods generated by Client.Macro. Should I define behaviour for every dynamically generated method (maybe also dynamically)? or there is no other way but to define ad-hoc mock module? Right now I'm using exvcr for all external request but I know it's not very clean.

P.S. Actually there are a lot of apis whose methods are the same except for their names so I think I'm not the only one having problems mocking them.

@josevalim
Copy link
Member

One of the guidelines in Elixir is that every time you you generate a function in the user code it should be to abide to some behaviour, so yes, you need behaviours.

Please see discussion in #9.

@josevalim
Copy link
Member

Another approach is for you to use atoms instead of defining functions, so you instead of SomeModule.foo, you would do SomeModule.request(:foo) or similar. This way you have a single interface and no longer rely on the function generation.

@ayrat555
Copy link
Author

ayrat555 commented Oct 6, 2017

Thanks, I guess, my approach with dynamic code generation was wrong

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants