测试 Elixir/Phoenix 个服务模块
Testing Elixir/Phoenix Service modules
我一直在研究 Elixir/Phoenix 第三方模块。 (用于从第三方服务获取一些数据的模块)其中一个模块如下所示:
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
在dev
和test
环境中分别配置@twiter_url
,但我想也能够测试来自 Twitter API 的真实响应,这将改变整个测试环境的 URL。
我想到的解决方案之一是
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
但这看起来很老套和愚蠢,必须有更好的解决方案。
config
在这里听起来是个好方法。您可以在测试运行时修改配置中的值,然后在测试后恢复它。
首先,在您的实际代码中,使用 Application.get_env(:my_app, :twitter_url)
.
而不是 @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
确保您没有为此使用异步测试,因为此技术会修改全局环境。
正如 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。)
我一直在研究 Elixir/Phoenix 第三方模块。 (用于从第三方服务获取一些数据的模块)其中一个模块如下所示:
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
在dev
和test
环境中分别配置@twiter_url
,但我想也能够测试来自 Twitter API 的真实响应,这将改变整个测试环境的 URL。
我想到的解决方案之一是
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
但这看起来很老套和愚蠢,必须有更好的解决方案。
config
在这里听起来是个好方法。您可以在测试运行时修改配置中的值,然后在测试后恢复它。
首先,在您的实际代码中,使用 Application.get_env(:my_app, :twitter_url)
.
@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
确保您没有为此使用异步测试,因为此技术会修改全局环境。
正如 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。)