wfgilman

wfgilman

Can you define a behaviour in the same module that implements it?

I have a behaviour for which I need to implement real functions and mock versions for testing. Right now I am using three modules to do this: the behaviour, the live and the mock. It makes my file structure and naming kind of a pain.

Is something like this possible?

def MyApp.MyModule do
  @behaviour MyApp.MyModule

  @callback myfunction(atom, list) :: :ok | :error
  def myfunction(arg, opts) do
    ...
  end
end

The Ecto.Repo behaviour has something similar where the default function implementations are in a __using__/1 macro, but I don’t need any code injection.

If not, why?

Most Liked

wfgilman

wfgilman

I realized that after I submitted the topic late Friday afternoon :joy:

The mocks are for an application that talks to a third party API. I’m trying to implement the application in a way that allows for proper testing.

I built an Elixir library for Plaid, the financial data service. I’m using this application as part of an umbrella application that is my larger project. I’ve since refactored the Plaid application to implement the recommended best practice for testing functions that call a third party. Here is a gist that shows the implementation for the Plaid /connect endpoint refactored to use this best practice. A single endpoint contains three files in the following directory structure:

+ lib
   + plaid
     + connect
       - live.ex
       - mock.ex
     - connect.ex

Plaid.Connect.ex is the behaviour and documentation
Plaid.Connect.Live.ex is the implementation
Plaid.Connect.Mock.ex is the mock

In my dev and test environments I can reference the appropriate module using the config.

This works great, but it seems like it can be refactored to something like I outlined in my first post. I’m so interested in doing this because I’d like to deploy this testing practice in other applications of my umbrella project. All the applications talk to each other, and I need to mock those client API functions in order to write proper unit tests. I’d like to be able to do so in a way that allows me to enforce explicit contracts without creating a complicated directory structure. If I can put the behaviour and implementation in the same module, then I can put all the mocks in another location (like a testing folder) which makes things clearer:

+ lib
  + plaid
    - connect.ex
+ test
  + lib
  + support
    + mock
      - connect.ex

Plaid.Connect.ex is the behaviour and implementation
Plaid.Mock.Connect.ex is the mock

hubertlepicki

hubertlepicki

I use behaviours with my modules exactly the same way, and the reason is also testing. I am using mox for testing and I do declare my behaviours and implementations in the same file. In fact, a behaviour is declared next to the function I am about to write.

I just use the behaviour to make sure my mocks follow the same interface as implementations.

wfgilman

wfgilman

bump :blush:

Where Next?

Popular in Questions Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
New
Tee
can someone please explain to me how Enum.reduce works with maps
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
mgjohns61585
Could someone help me? I’m making my first elixir program, number guessing game. I can’t figure out how to convert the user’s guess from ...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
dotdotdotPaul
Okay, I’m having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I’m sure I’...
New
yawaramin
In the Dialyzer docs ( dialyzer — OTP 29.0.2 (dialyzer 6.0.1) ), there is a way to turn off a specific warning for a function: -dialyzer...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
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

We're in Beta

About us Mission Statement