GithubHelp home page GithubHelp logo

stephanos / rewire Goto Github PK

View Code? Open in Web Editor NEW
97.0 4.0 6.0 75 KB

Dependency injection for Elixir. Zero code changes required.

Home Page: https://hex.pm/packages/rewire

License: Apache License 2.0

Elixir 100.00%
elixir dependency-injection mocking

rewire's Issues

Is it possible to rewire deps with the same unscoped module name as module under test?

Given two modules:

  • App.Connector.Hubspot (used by app to interact with external resource)
  • App.Client.Hubspot (used by connector to make external calls)

It is not possible to (easily?) inject a mock for the client in the connector tests, since:

  • elixir does not support the syntax: rewire App.Connector.Hubspot, App.Client.Hubspot: HubspotMock
  • aliasing App.Client.Hubspot is ambiguous since the connector has the same module name

It seems plausible it could work with some alias tweaking, but this could be more easily solved by accepting a list of tuples instead of a keyword list, perhaps? Hopefully I can look more into this later.

Rewire doesn't work with cover

I've tried using rewire and test coverage but I always get an error on the output of what happened. The cover files get created, and the coverage gets reported, but the full execution fails.

Analysis includes data from imported files
[".../Elixir.CloudWatch.R8837-20673.coverdata"]
** (MatchError) no match of right hand side value: {:error, {:file, <<70, 79, 82, 49, 0, 0, 12, 196, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 1, 218, 0, 0, 0, 42, 16, 69, 108, 105, 120, 105, 114, 46, 83, 84, 83, 46, 82, 54, 48, 48, 48, 8, 95, 95, 105, 110, ...>>, :badarg}}
    (mix 1.15.0) lib/mix/tasks/test.coverage.ex:292: anonymous fn/3 in Mix.Tasks.Test.Coverage.html/2
    (elixir 1.15.0) lib/enum.ex:2510: Enum."-reduce/3-lists^foldl/2-0-"/3
    (mix 1.15.0) lib/mix/tasks/test.coverage.ex:291: Mix.Tasks.Test.Coverage.html/2
    (mix 1.15.0) lib/mix/tasks/test.ex:578: Mix.Tasks.Test.do_run/3
    (mix 1.15.0) lib/mix/task.ex:447: anonymous fn/3 in Mix.Task.run_task/5
    (mix 1.15.0) lib/mix/task.ex:502: Mix.Task.run_alias/6
    (mix 1.15.0) lib/mix/project.ex:458: Mix.Project.in_project/4
    (elixir 1.15.0) lib/file.ex:1624: File.cd!/2
    (mix 1.15.0) lib/mix/task.ex:604: anonymous fn/4 in Mix.Task.run_in_children_projects/2
    (elixir 1.15.0) lib/enum.ex:2510: Enum."-reduce/3-lists^foldl/2-0-"/3
    (mix 1.15.0) lib/mix/task.ex:603: Mix.Task.run_in_children_projects/2
    (mix 1.15.0) lib/mix/project_stack.ex:237: Mix.ProjectStack.recur/1
    (mix 1.15.0) lib/mix/cli.ex:92: Mix.CLI.run_task/2
    (elixir 1.15.0) lib/code.ex:1432: Code.require_file/2

Is it possible to rewrite modules using `alias Foo, as: Bar`?

Hey there! I found this library via this blog post and wanted to include it in my project.

One thing I've found is that it doesn't seem to support rewiring modules where the modules must us as:. Here's an example based on a real-world issue that I'm running up against during a refactor:

defmodule Foo do
  # See they have the same final name part so I _must_ use `as:`
  alias Foo.YtDlp.CommandRunner, as: YtDlpRunner
  alias Foo.Apprise.CommandRunner, as: AppriseRunner

  def yt_dlp_bar, do: YtDlpRunner.bar()
  def apprise_baz, do: AppriseRunner.baz()
  # ...
end

These are both modules that interface with CLI programs. Currently I'm using the standard Mox-recommended method of DI, but I dislike that it puts testing concerns in my main app code. I was hoping I could do something like this using rewire in my tests:

rewire Foo, YtDlpRunner: YtDlpRunnerMock, AppriseRunner: AppriseRunnerMock

# ...

But that doesn't seem to be possible. I could be missing something, but in my testing it seems like the AST is functionally rewritten to this:

defmodule Foo do
  alias Foo.YtDlp.CommandRunner, as: YtDlpRunnerMock
  alias Foo.Apprise.CommandRunner, as: AppriseRunnerMock

  def yt_dlp_bar, do: YtDlpRunnerMock.bar()
  def apprise_baz, do: AppriseRunnerMock.baz()
  # ...
end

Which only overwrites the as: alias, but it's still ultimately pointing to the original modules.


Is there a way to handle this and I just missed it? If not, would you be open to a PR that handles this case? This would be a breaking change but I think it's a valuable one

Missing code coverage

One downside of running tests against a rewired copy of the actual module is that code coverage data is not available anymore. That's because Erlang's cover modifies tested modules by injecting custom instructions to track the coverage.

rewire should report the correct test coverage.

prior art: https://github.com/edgurgel/mimic/blob/c7529f8f9607d1ef0725dc723c5337531e5f8326/lib/mimic/cover.ex + https://github.com/eproxus/meck/blob/2c7ba603416e95401500d7e116c5a829cb558665/src/meck_proc.erl

Rewire nested module support?

Thanks a lot for the library, it's a very cool idea to utilise aliasing and I really enjoy not having to deal with Application.fetch!

However, I am facing an issue with nested modules right now.

In my example,

defmodule Messaging do
  def send, do: Courier.send()
end

defmodule Courier do
  def send, do: Pigeon.send()
end

Pigeon is the mock that I want to implement, now, if I do:

defmock(PigeonMock, for: PigeonBehaviour)
rewire Messaging, Pigeon: PigeonMock

This compiler gives a nice helpful message and says it can't find Pigeon in Messaging, which is only available in Courier.

My actual use case is a little more complicated (with protocol dispatch), but this should illustrate the point just fine.

I can see that it is mentioned in the README,

Only the dependencies of the rewired module will be replaced. Any modules defined around the rewired module will be ignored. All references of the rewired module to them will be pointing to the original. You're always able to rewire them separately yourself.

But is this due to a technical reason, or will it be possible to do it? I'd gladly take a look at it if that's the case.

Since rewire works by replacing the module with a new version with alias, perhaps we could just recursively walk and replace the modules?

I haven't really looked into where it would work or not, but the proposed idea could look something like:

rewire Messaging, [Courier: [Pigeon: PigeonMock]]

This generates a new Messaging module that uses a rewired version of Courier, which uses a rewired version of Pigeon

rewire doesn't work through `use`

When rewiring a module in a test, we were noticing that it wasn't replacing all of the occurrences of a module. We eventually tracked down that the module in question was being brought in by a use macro. When traversing the AST, it appears that the use special form doesn't get expanded for rewire, so anything that brings in is left with the original module.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.