• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

parroty / exvcr / f6cc9bc0ae05f24b374e6c1fa9b52e9d0967e032-PR-202

pending completion
f6cc9bc0ae05f24b374e6c1fa9b52e9d0967e032-PR-202

Pull #202

github

parroty
Pull Request #202: Fix name conflict for finch test case

577 of 642 relevant lines covered (89.88%)

95.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.0
/lib/exvcr/mock.ex
1
defmodule ExVCR.Mock do
2
  @moduledoc """
3
  Provides macro to record HTTP request/response.
4
  """
5

6
  alias ExVCR.Recorder
7

8
  defmacro __using__(opts) do
9
    adapter = opts[:adapter] || ExVCR.Adapter.IBrowse
17✔
10
    options = opts[:options]
17✔
11

12
    quote do
13
      import ExVCR.Mock
14
      :application.start(unquote(adapter).module_name)
15
      use unquote(adapter)
16

17
      def adapter_method() do
18
        unquote(adapter)
19
      end
20

21
      def options_method() do
22
        unquote(options)
23
      end
24
    end
25
  end
26

27
  @doc """
28
  Provides macro to trigger recording/replaying http interactions.
29

30
  ## Options
31

32
  - `:match_requests_on` A list of request properties to match on when
33
    finding a matching response. Valid values include `:query`, `:headers`,
34
    and `:request_body`
35

36
  """
37
  defmacro use_cassette(:stub, options, test) do
38
    quote do
39
      stub_fixture = "stub_fixture_#{ExVCR.Util.uniq_id}"
40
      stub = prepare_stub_record(unquote(options), adapter_method())
41
      recorder = Recorder.start([fixture: stub_fixture, stub: stub, adapter: adapter_method()])
42

43
      try do
44
        mock_methods(recorder, adapter_method())
45
        [do: return_value] = unquote(test)
46
        return_value
47
      after
48
        module_name = adapter_method().module_name
49
        unload(module_name)
50
        ExVCR.MockLock.release_lock()
51
      end
52
    end
53
  end
54

55
  defmacro use_cassette(fixture, options, test) do
56
    quote do
57
      recorder = Recorder.start(
58
        unquote(options) ++ [fixture: normalize_fixture(unquote(fixture)), adapter: adapter_method()])
59

60

61
      try do
62
        mock_methods(recorder, adapter_method())
63
        [do: return_value] = unquote(test)
64
        return_value
65
      after
66
        recorder_result = Recorder.save(recorder)
67

68
        module_name = adapter_method().module_name
69
        unload(module_name)
70
        ExVCR.MockLock.release_lock()
71

72
        recorder_result
73
      end
74
    end
75
  end
76

77
  defmacro use_cassette(fixture, test) do
78
    quote do
79
      use_cassette(unquote(fixture), [], unquote(test))
80
    end
81
  end
82

83
  @doc false
84
  defp load(adapter, recorder) do
85
    if ExVCR.Application.global_mock_enabled?() do
158✔
86
      ExVCR.Actor.CurrentRecorder.set(recorder)
158✔
87
    else
88
      module_name    = adapter.module_name
×
89
      target_methods = adapter.target_methods(recorder)
×
90
      Enum.each(target_methods, fn({function, callback}) ->
×
91
        :meck.expect(module_name, function, callback)
×
92
      end)
93
    end
94
  end
95

96
  @doc false
97
  def unload(module_name) do
98
    if ExVCR.Application.global_mock_enabled?() do
157✔
99
      ExVCR.Actor.CurrentRecorder.default_state()
100
      |> ExVCR.Actor.CurrentRecorder.set()
157✔
101
    else
102
      :meck.unload(module_name)
×
103
    end
104
  end
105

106
  @doc """
107
  Mock methods pre-defined for the specified adapter.
108
  """
109
  def mock_methods(recorder, adapter) do
110
    parent_pid = self()
158✔
111
    Task.async(fn ->
112
      ExVCR.MockLock.ensure_started
158✔
113
      ExVCR.MockLock.request_lock(self(), parent_pid)
158✔
114
      receive do
158✔
115
        :lock_granted ->
116
          load(adapter, recorder)
158✔
117
      end
118
    end)
119
    |> Task.await(:infinity)
158✔
120
  end
121

122
  @doc """
123
  Prepare stub record based on specified option parameters.
124
  """
125
  def prepare_stub_record(options, adapter) do
126
    method        = (options[:method] || "get") |> to_string
17✔
127
    url           = (options[:url] || "~r/.+/") |> to_string
17✔
128
    body          = (options[:body] || "Hello World") |> to_string
17✔
129
    # REVIEW: would be great to have "~r/.+/" as default request_body
130
    request_body  = (options[:request_body] || "") |> to_string
17✔
131

132
    headers     = options[:headers] || adapter.default_stub_params(:headers)
17✔
133
    status_code = options[:status_code] || adapter.default_stub_params(:status_code)
17✔
134

135
    record = %{ "request"  => %{"method" => method, "url" => url, "request_body" => request_body},
17✔
136
                "response" => %{"body" => body, "headers"  => headers, "status_code" => status_code} }
137

138
    [adapter.convert_from_string(record)]
139
  end
140

141
  @doc """
142
  Normalize fixture name for using as json file names, which removes whitespaces and align case.
143
  """
144
  def normalize_fixture(fixture) do
145
    fixture |> String.replace(~r/\s/, "_") |> String.downcase
141✔
146
  end
147
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc