0

我一直在玩 Elixir/Phoenix 第三方模块。(用于从 3rd 方服务获取一些数据的模块)其中一个模块看起来像这样:

module TwitterService do
  @twitter_url "https://api.twitter.com/1.1"

  def fetch_tweets(user) do
     # The actual code to fetch tweets
     HTTPoison.get(@twitter_url)
     |> process_response
  end      

  def process_response({:ok, resp}) do
    {:ok, Poison.decode! resp}
  end

  def process_response(_fail), do: {:ok, []}
end

实际数据在我的问题中并不重要。所以现在,我对如何在测试中动态配置@twitter_url模块变量以使某些测试故意失败很感兴趣。例如:

module TwitterServiceTest
  test "Module returns {:ok, []} when Twitter API isn't available"
    # I'd like this to be possible ( coming from the world of Rails )
    TwitterService.configure(:twitter_url, "new_value") # This line isn't possible
    # Now the TwiterService shouldn't get anything from the url
    tweets = TwitterService.fetch_tweets("test")
    assert {:ok, []} = tweets
  end
end

我怎样才能做到这一点? 注意:我知道我可以在和环境中单独:configs配置,但我也希望能够测试来自 Twitter API 的真实响应,这会改变整个测试环境的 URL。 我想出的解决方案之一是@twiter_urldevtest

def fetch_tweets(user, opts \\ []) do
  _fetch_tweets(user, opts[:fail_on_test] || false)
end

defp _fetch_tweets(user, [fail_on_test: true]) do
  # Fails
end

defp _fetch_tweets(user, [fail_on_test: false]) do
  # Normal fetching
end

但这似乎很愚蠢和愚蠢,必须有更好的解决方案。

4

2 回答 2

2

正如 José 在Mocks And Explicit Contracts中所建议的那样,最好的方法可能是使用依赖注入:

module TwitterService do
  @twitter_url "https://api.twitter.com/1.1"

  def fetch_tweets(user, service_url \\ @twitter_url) do
     # The actual code to fetch tweets
     service_url
     |> HTTPoison.get()
     |> process_response
  end      

  ...
end

现在在测试中,您只需在必要时注入另一个依赖项:

# to test against real service
fetch_tweets(user)

# to test against mocked service
fetch_tweets(user, SOME_MOCK_URL)

这种方法还将使将来更容易插入不同的服务。处理器实现不应该依赖于它的底层服务,假设服务遵循一些合同(在这种特殊情况下,用给定 url 的 json 响应。)

于 2017-12-16T16:16:31.237 回答
1

config在这里听起来是个好方法。您可以在测试运行时修改配置中的值,然后在测试后恢复它。

首先,在您的实际代码中@twitter_url,使用Application.get_env(:my_app, :twitter_url).

然后,在您的测试中,您可以使用如下包装函数:

def with_twitter_url(new_twitter_url, func) do
  old_twitter_url = Application.get_env(:my_app, :twitter_url)
  Application.set_env(:my_app, :twitter_url, new_twitter_url)
  func.()
  Application.set_env(:my_app, :twitter_url, old_twitter_url)
end

现在在您的测试中,请执行以下操作:

with_twitter_url "<new url>", fn ->
  # All calls to your module here will use the new url.
end

确保您没有为此使用异步测试,因为此技术会修改全局环境。

于 2017-12-16T14:24:14.323 回答