Repository: edgurgel/mimic (elixir)

Issue:
Being able to call the original mocked function would be amazing.
There are many cases where I want to call the original function some number of times before then wanting to do something different, or perhaps wanting to call the original function conditionally dependent on the arguments.

I'm not aware of a current way of grabbing the original function. When I attempt this:

```elixir
original = &MyModule.function/3
```

even if done before calling `expect`, I get the mocked function.

Pull request description:
Add functionality to call the original function
As has been suggested in #61, this adds an option to call original version of the function.

Fixes #61

Patch:
diff --git a/README.md b/README.md
index f960ded..3f8420e 100644
--- a/README.md
+++ b/README.md
@@ -115,7 +115,7 @@ Calculator
 verify!()
 ```
 
-Using `expect/4` on intra-module functions will not work, unless the function is referenced by it's fully qualified name. 
+Using `expect/4` on intra-module functions will not work, unless the function is referenced by it's fully qualified name.
 
 ```elixir
 defmodule Calculator do
@@ -206,7 +206,7 @@ To use DSL Mode `use Mimic.DSL` rather than `use Mimic` in your test.  DSL Mode
 ```
 
 ## Stubs with fake module
-`stub_with/2` enable substitute function call of a module with another similar module
+`stub_with/2` enable substitute function call of a module with another similar module.
 
 ```elixir
   defmodule BadCalculator do
@@ -222,6 +222,18 @@ To use DSL Mode `use Mimic.DSL` rather than `use Mimic` in your test.  DSL Mode
   end
 ```
 
+## Calling the original
+`call_original/3` allows to call original unmocked version of the function.
+
+```elixir
+setup :set_mimic_private
+
+test "calls original function even if it has been is stubbed" do
+  stub_with(Calculator, InverseCalculator)
+
+  assert call_original(Calculator, :add, [1, 2]) == 3
+end
+```
 
 ## Implementation Details & Performance
 
diff --git a/lib/mimic.ex b/lib/mimic.ex
index c36e5bf..49b1790 100644
--- a/lib/mimic.ex
+++ b/lib/mimic.ex
@@ -374,6 +374,38 @@ defmodule Mimic do
     end
   end
 
+  @doc """
+  Call original implementation of a function.
+
+  This function allows you to call the original implementation of a function,
+  even if it has been stubbed, rejected or expected.
+
+  ## Arguments:
+
+    * `module` - the name of the module in which we're calling.
+    * `function_name` - the name of the function we're calling.
+    * `args` - the arguments of the function we're calling.
+
+  ## Raises:
+
+    * If `function_name` does not exist in `module`.
+
+  ## Example:
+
+      iex> Mimic.call_original(Calculator, :add, [1, 2])
+      3
+
+  """
+  @spec call_original(module, atom, list) :: any
+  def call_original(module, function_name, args) do
+    arity = length(args)
+
+    raise_if_not_exported_function!(module, function_name, arity)
+    func = Function.capture(Mimic.Module.original(module), function_name, arity)
+
+    Kernel.apply(func, args)
+  end
+
   @doc """
   Verifies the current process after it exits.
 


Test patch:
diff --git a/test/mimic_test.exs b/test/mimic_test.exs
index 2b7e43b..b2788d2 100644
--- a/test/mimic_test.exs
+++ b/test/mimic_test.exs
@@ -924,7 +924,7 @@ defmodule Mimic.Test do
     end
   end
 
-  describe "copy/1 with duplicates does nothing" do
+  describe "copy/1 with duplicates" do
     setup :set_mimic_private
 
     test "stubs still stub" do
@@ -946,6 +946,44 @@ defmodule Mimic.Test do
     end
   end
 
+  describe "call_original/3" do
+    setup :set_mimic_private
+
+    test "calls original function even if it has been is stubbed" do
+      stub_with(Calculator, InverseCalculator)
+
+      assert call_original(Calculator, :add, [1, 2]) == 3
+    end
+
+    test "calls original function even if it has been rejected as a module function" do
+      Mimic.reject(Calculator, :add, 2)
+
+      assert call_original(Calculator, :add, [1, 2]) == 3
+    end
+
+    test "calls original function even if it has been rejected as a capture" do
+      Mimic.reject(&Calculator.add/2)
+
+      assert call_original(Calculator, :add, [1, 2]) == 3
+    end
+
+    test "when called on a function that has not been stubbed" do
+      assert call_original(Calculator, :add, [1, 2]) == 3
+    end
+
+    test "when called on a module that does not exist" do
+      assert_raise ArgumentError, "Function add/2 not defined for NonExistentModule", fn ->
+        call_original(NonExistentModule, :add, [1, 2])
+      end
+    end
+
+    test "when called on a function that does not exist" do
+      assert_raise ArgumentError, "Function non_existent_call/2 not defined for Calculator", fn ->
+        call_original(Calculator, :non_existent_call, [1, 2])
+      end
+    end
+  end
+
   describe "structs" do
     setup :set_mimic_private