2016-12-07 60 views
2

我有一个GenServer,负责联系外部资源。调用外部资源的结果并不重要,有时可能会出现故障,因此使用handle_cast似乎适用于其他代码部分。我的外部资源有一个类似界面的模块,我使用一个GenServer来访问资源。到现在为止还挺好。如何正确测试GenServer中的handle_cast?

但是,当我试图写这个gen_server测试,我无法弄清楚如何测试handle_cast。我有GenServer的接口功能,我试图测试那些,但除非GenServer未运行,否则它们总是返回:ok。我无法测试。

我改了一下代码。我将handle_cast中的代码抽象为另一个函数,并创建了一个类似的handle_call回调。然后我可以很容易地测试handle_call,但那是一种破解。

我想知道人们通常如何测试异步代码。我的方法是正确的还是可以接受的?如果不是,那么该怎么办?

+0

你想什么在这里测试的'handle_cast'?它能够初步接触外部资源?或者它也成功了?您是否想在每次测试运行期间实际联系该外部资源? (代码的一些粗略概述也会有帮助。) – Dogbert

+0

不,我将外部资源接口模块注入GenServer,所以我不想实际联系资源(这会花我钱)。由于'handle_cast'使用了接口模块的一些功能,我想测试它在函数返回好的值时成功。添加一些代码是一个好主意,我会在今天几个小时内完成。 – vfsoraki

回答

3

更多从:sys模块等实用功能的投申请的形式为:

Module:handle_cast(Request, State) -> Result 

Types: 
Request = term() 
State = term() 
Result = {noreply,NewState} | 
     {noreply,NewState,Timeout} | 
     {noreply,NewState,hibernate} | 
     {stop,Reason,NewState} 
NewState = term() 
Timeout = int()>=0 | infinity 
Reason = term() 

所以它是很容易进行单元测试只是直接调用它(无需启动服务器),提供RequestState,并断言返回的Result。当然,它也可能有一些副作用(如在ets表中编写,修改过程字典...),因此您需要先初始化这些资源,并在assert之后检查效果。

例如:

test_add() -> 
    {noreply,15} = my_counter:handle_cast({add,5},10). 
+0

在我的情况下,似乎正确的解决方案,我的接口函数只是作为'handle_ *'函数的委托。 IDK介绍其他具有更复杂接口函数的用例,但这会解决我的问题。谢谢! – vfsoraki

2

诀窍是记住一个GenServer进程依次处理消息。这意味着我们可以确保流程收到并处理了一条消息,确保它处理了我们稍后发送的消息。这反过来意味着我们可以将任何异步操作更改为同步操作,方法是使用同步消息(例如某个调用)跟随它。

测试场景是这样的:

  1. 发出异步请求。
  2. 发出同步请求并等待结果。
  3. 断言异步请求的影响。

如果服务器不具有同步的任何合适的功能,你可以考虑使用:sys.get_state/2 - 呼叫用于调试的目的,那就是正确的,所有特殊工艺(包括GenServer)处理,是,什么是可能的最重要的是,同步。我认为使用它进行测试是完全有效的。

您可以阅读GenServer documentation

+0

好点,1&2.但是3有点不清楚,如何测试一些正在另一个过程中完成的效果?似乎这些案件没有通用的方式,因为每个案件都可能有或可能没有副作用。即使那些拥有,可能有基本不同的,所以断言会有所不同。 – vfsoraki