- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在编写一个模块来查询在线天气 API。我决定将它作为一个应用程序来实现,并带有一个受监督的 GenServer
.
这是代码:
defmodule Weather do
use GenServer
def start_link() do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
def weather_in(city, country) do
GenServer.call(__MODULE__, {:weather_in, city, country_code})
end
def handle_call({:weather_in, city, country}) do
# response = call remote api
{:reply, response, nil}
end
end
setup
回调以启动服务器:
defmodule WeatherTest do
use ExUnit.Case
setup do
{:ok, genserver_pid} = Weather.start_link
{:ok, process: genserver_pid}
end
test "something" do
# assert something using Weather.weather_in
end
test "something else" do
# assert something else using Weather.weather_in
end
end
GenServer
使用特定名称有几个原因:
Weather
中定义一个公共(public) API抽象底层 GenServer
存在的模块.用户不必为 weather_in
提供 PID/名称。与底层通信的函数GenServer
GenServer
在监督树下setup
每个测试执行一次回调。因此,有并发尝试启动我的服务器,它失败了
{:error, {:already_started, #PID<0.133.0>}}
.
GenServer
时,我有以下选择:
async: false
)并且每个测试必须开始 和 终止服务器。 setup_all
,对于整个测试用例,一个实例只能启动一次)。然而,恕我直言,这是一种错误的测试方法,因为所有测试都将针对同一台服务器运行,改变其状态并因此相互混淆。 :already_started
.我再次阅读了有关
async: false
的文档并发现它阻止了
测试用例 与其他测试用例并行运行。它没有像我想的那样按顺序运行我的测试用例的测试。
最佳答案
我注意到的一个关键问题是您的 handle_call
签名错误。 ,应该是 handle_call(args, from, state)
(您目前只有 handle_call(args)
。
我从未使用过它,但我仰慕的人发誓 QuickCheck 是真正测试 GenServer 的黄金标准。
在单元测试级别,由于 GenServer 的功能架构,存在另一个选项:
如果您测试 handle_[call|cast|info]
具有预期参数和状态组合的方法,您不必*启动 GenServer:使用您的测试库来替换 OTP,并像调用平面库一样调用您的模块代码。这不会测试您的 api 函数调用,但如果您将它们保留为精简的传递方法,则可以将风险降至最低。
*如果你使用延迟回复,你会遇到一些问题,但你可以通过足够的工作来解决它们。
我对您的 GenServer 进行了一些更改:
defmodule Weather do
use GenServer
def start_link() do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def weather_in(city, country) do
GenServer.call(__MODULE__, {:weather_in, city, country_code})
end
def upgrade, do: GenServer.cast(__MODULE__, :upgrade)
def downgrade, do: GenServer.cast(__MODULE__, :downgrade)
defmodule State do
defstruct url: :regular
end
def init([]), do: {:ok, %State{}}
def handle_cast(:upgrade, state) do
{:noreply, %{state|url: :premium}}
end
def handle_cast(:downgrade, state) do
{:noreply, %{state|url: :regular}}
end
# Note the proper signature for handle call:
def handle_call({:weather_in, city, country}, _from, state) do
response = case state.url do
:regular ->
#call remote api
:premium ->
#call premium api
{:reply, response, state}
end
end
# assumes you can mock away your actual remote api calls
defmodule WeatherStaticTest do
use ExUnit.Case, async: true
#these tests can run simultaneously
test "upgrade changes state to premium" do
{:noreply, new_state} = Weather.handle_cast(:upgrade, %Weather.State{url: :regular})
assert new_state.url == :premium
end
test "upgrade works even when we are already premium" do
{:noreply, new_state} = Weather.handle_cast(:upgrade, %Weather.State{url: :premium})
assert new_state.url == :premium
end
# etc, etc, etc...
# Probably something similar here for downgrade
test "weather_in using regular" do
state = %Weather.State{url: :regular}
{:reply, response, newstate} = Weather.handle_call({:weather_in, "dallas", "US"}, nil, state)
assert newstate == state # we aren't expecting changes
assert response == "sunny and hot"
end
test "weather_in using premium" do
state = %Weather.State{url: :premium}
{:reply, response, newstate} = Weather.handle_call({:weather_in, "dallas", "US"}, nil, state)
assert newstate == state # we aren't expecting changes
assert response == "95F, 30% humidity, sunny and hot"
end
# etc, etc, etc...
end
关于elixir - Elixir 中 GenServer 的惯用测试策略是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33018952/
我在自动启动监督树时遇到了死锁问题。一个 GenServer 的初始状态是树中另一个主管的子进程。代码如下: 主管和 worker : defmodule Parallel.Worker.Superv
考虑这样简单的 GenServer 模块: defmodule Crap do use GenServer ... #All the needed crap def handle_info
背景 我有一组测试需要先启动 GenServer。根据经验,我知道在每次测试后进行清理是一种很好的做法,因此我也想在每次测试后停止 GenServer。 问题 这里的问题是我不知道如何在测试完成后停止
在elixir GenServer中,有sync和async方法,handle_cast和handle_call。在异步情况下,如果该方法失败,我如何获得通知? 方法失败意味着在handle_call
我对一些 OTP 概念不熟悉。我有 GenServer,它将向 RabbitMQ 发布事件。此 GenServer 具有状态:amqp Chanel,它在 init() 期间启动一次,并在 cast
我缩小了问题的大小,因为它太大了。这是代码: defmodule MayRaiseGenServer do use GenServer def start_link do IO.put
我正在练习这个例子。 https://github.com/kwmiebach/how-to-elixir-supervisor 我按照说明进行操作并了解它是如何工作的,但我无法理解 Supervis
我的一项服务与速率受限的外部 API 进行通信,因此我想确保每 10 秒发送的调用不超过 1 个。 我的天真的方法是拥有一个长期运行的 API 服务,并在每次调用后超时: def handle_cas
我最近遇到了一点麻烦:GenServer 进程使用的内存非常高,可能是因为大量的二进制泄漏。 问题来自于这里:我们通过 GenServer 接收大型二进制文件,并将它们传递给消费者,然后消费者与该数据
我知道我可以像这样调用 GenServer GenServer.call(pid, request) # using a pid 或者像这样 GenServer.call(registered_nam
我的一项服务与速率受限的外部 API 进行通信,因此我想确保每 10 秒发送的调用不超过 1 个。 我的天真的方法是拥有一个长期运行的 API 服务,并在每次调用后超时: def handle_cas
假设我有 GenServer 实例: defmodule MyModule do use GenServer def init(_) do {:ok, %{}} end #... e
我有一个为单个项目实现功能的 GenServer,例如: def handle_call({:sync, id}, _from, state) do ## update data {:
给定一个简单的 GenServer过程。 defmodule KVServer do use GenServer def start do GenServer.start(__MODU
我正在编写一个模块来查询在线天气 API。我决定将它作为一个应用程序来实现,并带有一个受监督的 GenServer . 这是代码: defmodule Weather do use GenServ
场景: 我有一个简单的GenServer来管理某些状态。 目前,我正在使用 map 来管理我的状态。但它正在增长我正在向该状态添加更多数据。 问题: 那么,为了获得一些编译时保证,我的 GenServ
我正在阅读this关于如何从现有的phoenix应用程序中提取微服务的文章。作者重构了 phoenix 应用程序 Controller 之一,并将其方法之一移至 Genserver,之后将该 Ge
我有一个模拟应用程序范围的 Repo 的测试。大多数时候,测试都是绿色的。当我在循环中运行测试时,使用相同的种子,可能有 10% 的运行成功,但带有 GenServer 终止消息: 15:39:34.
我有一个 GenServer,它负责联系外部资源。调用外部资源的结果并不重要,偶尔失败是可以接受的,所以使用 handle_cast 似乎适用于代码的其他部分。我确实有一个用于该外部资源的类似接口(i
在这两种方法中,我都坚持如何通过给定的一组 id 或组映射进程,然后将存储的结构映射到过滤数据。%{group => [users]}执行。 我意识到组将与用户相反,因此我创建了一个使用组名称作为键的
我是一名优秀的程序员,十分优秀!