Elixir Application.set_env 和并发竞争条件
Elixir Application.set_env and concurrency race condition
阅读 http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/ 文章后,我的代码中有:
defp rest_adapter, do: Application.get_env(:app_name, :rest_adapter)
我在测试期间使用它 "mock" 休息适配器和 return 不同的结果和错误代码。
但是,在这些测试期间,存在竞争条件,因为我为不同的测试用例设置了不同的 rest_adapter
。
有时它们会按预期工作,但有时它们不会 "catch" 不同 rest_adapter
专门为此测试设置。
如何避免那里的问题?
问题出在应用程序环境是全局的这一事实 - 您不能同时在多个地方更改它以具有不同的值。
最简单的解决方案是通过删除 async: true
来禁用并发测试,但是,这会使您的测试变慢,因为它们不能同时 运行。
幸运的是还有其他可能的解决方案。最简单的(也可以说是最优雅的)是将适配器作为选项传递给使用它的函数,并且在提供 none 时使用来自应用程序环境的适配器。另一个解决方案,当调用发生在同一个进程中时(这种情况经常发生)是使用进程字典来传递适配器,而不是依赖于应用程序环境。
此外,可以采用混合解决方案,例如有两个适配器:一个真实的适配器和一个从进程字典返回响应的适配器。然后在测试中始终使用进程字典,并在调用函数之前放置预期的响应。
最后,像 Bypass 这样的东西在稍微不同的层上工作,为您提供模拟的 HTTP 端点,而不是替换调用端点的代码(这是 "replace" 的常用方法测试中的 http 调用)。
阅读 http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/ 文章后,我的代码中有:
defp rest_adapter, do: Application.get_env(:app_name, :rest_adapter)
我在测试期间使用它 "mock" 休息适配器和 return 不同的结果和错误代码。
但是,在这些测试期间,存在竞争条件,因为我为不同的测试用例设置了不同的 rest_adapter
。
有时它们会按预期工作,但有时它们不会 "catch" 不同 rest_adapter
专门为此测试设置。
如何避免那里的问题?
问题出在应用程序环境是全局的这一事实 - 您不能同时在多个地方更改它以具有不同的值。
最简单的解决方案是通过删除 async: true
来禁用并发测试,但是,这会使您的测试变慢,因为它们不能同时 运行。
幸运的是还有其他可能的解决方案。最简单的(也可以说是最优雅的)是将适配器作为选项传递给使用它的函数,并且在提供 none 时使用来自应用程序环境的适配器。另一个解决方案,当调用发生在同一个进程中时(这种情况经常发生)是使用进程字典来传递适配器,而不是依赖于应用程序环境。
此外,可以采用混合解决方案,例如有两个适配器:一个真实的适配器和一个从进程字典返回响应的适配器。然后在测试中始终使用进程字典,并在调用函数之前放置预期的响应。
最后,像 Bypass 这样的东西在稍微不同的层上工作,为您提供模拟的 HTTP 端点,而不是替换调用端点的代码(这是 "replace" 的常用方法测试中的 http 调用)。