Concurrency 在erlang中测试
我试着在Erlang学习测试。我使用了目前一个tic-tac-toe游戏的代码库。这是一个两人游戏(让我们称之为一对玩家)。我基本上想检查当多对玩家同时玩这个游戏时会发生什么。 因此,我尝试编写一个简单的基本模块来测试它,名为Concurrency 在erlang中测试,concurrency,parallel-processing,erlang,erlang-shell,Concurrency,Parallel Processing,Erlang,Erlang Shell,我试着在Erlang学习测试。我使用了目前一个tic-tac-toe游戏的代码库。这是一个两人游戏(让我们称之为一对玩家)。我基本上想检查当多对玩家同时玩这个游戏时会发生什么。 因此,我尝试编写一个简单的基本模块来测试它,名为testing,如下所示: -module(testing). %% ==================================================================== %% API functions %% ============
testing
,如下所示:
-module(testing).
%% ====================================================================
%% API functions
%% ====================================================================
-export([all/0]).
%% ====================================================================
%% Internal functions
%% ====================================================================
all()->
test1(),
timer:sleep(75),
test2(),
timer:sleep(75),
test3(),
timer:sleep(75).
test1()->
G_serv_Output1=gameserver:start(),
io:format("Value of G_serv_Output in test1 is ~p~n",
[G_serv_Output1]),
Abhishek=gameclient:login("Abhishek"),
Dharun=gameclient:login("Dharun"),
gameclient:new_game(Abhishek,"Dharun"),
timer:sleep(5),
gameclient:make_move(Abhishek,"Dharun", a1),
timer:sleep(5),
gameclient:make_move(Dharun,"Abhishek", b2),
timer:sleep(5),
gameclient:make_move(Abhishek,"Dharun", a2),
timer:sleep(5),
gameclient:make_move(Dharun,"Abhishek", c3),
timer:sleep(5),
gameclient:make_move(Abhishek,"Dharun", a3).
test2()->
G_serv_Output1=gameserver:start(),
io:format("Value of G_serv_Output in test2 is ~p~n",
[G_serv_Output1]),
Abhijeet=gameclient:login("Abhijeet"),
Ranjan=gameclient:login("Ranjan"),
gameclient:new_game(Abhijeet,"Ranjan"),
timer:sleep(5),
gameclient:make_move(Abhijeet,"Ranjan", a1),
timer:sleep(5),
gameclient:make_move(Ranjan,"Abhijeet", b2),
timer:sleep(5),
gameclient:make_move(Abhijeet,"Ranjan", a2),
timer:sleep(5),
gameclient:make_move(Ranjan,"Abhijeet", c3),
timer:sleep(5),
gameclient:make_move(Abhijeet,"Ranjan", a3).
test3()->
G_serv_Output1=gameserver:start(),
io:format("Value of G_serv_Output in test3 is ~p~n",
[G_serv_Output1]),
Bana=gameclient:login("Bana"),
Sagar=gameclient:login("Sagar"),
gameclient:new_game(Bana,"Sagar"),
timer:sleep(5),
gameclient:make_move(Bana,"Sagar", a1),
timer:sleep(5),
gameclient:make_move(Sagar,"Bana", b2),
timer:sleep(5),
gameclient:make_move(Bana,"Sagar", a2),
timer:sleep(5),
gameclient:make_move(Sagar,"Bana", c3),
timer:sleep(5),
gameclient:make_move(Bana,"Sagar", a3).
new_game(Pid) ->
Pid ! {new_game}.
make_move(Pid, Move) ->
Pid ! { make_move, Move }.
loop(Name) ->
receive
{ new_game} ->
gameserver:new_game(Name),
loop(Name);
{ make_move, Move } ->
gameserver:make_move(Name, Move),
loop(Name)
end.
-module(testing2).
-include_lib("common_test/include/ct.hrl").
%% ====================================================================
%% API functions
%% ====================================================================
-export([all/0, groups/0, init_per_group/2, end_per_group/2]).
-export([test1/1,test2/1]).
%% ====================================================================
%% Internal functions
%% ====================================================================
all() -> [{group, session}].
groups() -> [{session,
[],
[{group, clients}]},
{clients,
[parallel, {repeat, 1}],
[test1, test2]}].
init_per_group(session, Config) ->
gameserver:start(),
Config;
init_per_group(_, Config) ->
Config.
end_per_group(session, _Config) ->
gameserver:stop();
end_per_group(_, _Config) ->
ok.
test1(_Config)->
Abhishek=gameclient:login("Abhishek"),
Dharun=gameclient:login("Dharun"),
gameclient:new_game(Abhishek),
timer:sleep(10),
gameclient:make_move(Abhishek, a1),
timer:sleep(10),
gameclient:make_move(Dharun, b2),
timer:sleep(10),
gameclient:make_move(Abhishek, a2),
timer:sleep(10),
gameclient:make_move(Dharun, c3),
timer:sleep(10),
gameclient:make_move(Abhishek, a3).
test2(_Config)->
Abhijeet=gameclient:login("Abhijeet"),
Ranjan=gameclient:login("Ranjan"),
gameclient:new_game(Abhijeet),
timer:sleep(10),
gameclient:make_move(Abhijeet, a1),
timer:sleep(10),
gameclient:make_move(Ranjan, b2),
timer:sleep(10),
gameclient:make_move(Abhijeet, a2),
timer:sleep(10),
gameclient:make_move(Ranjan, c3),
timer:sleep(10),
gameclient:make_move(Abhijeet, a3).
三次测试都进行得很好,我看到三场比赛都完成了
然后我注意到像gameclient:new_game/2
和gameclient:new_game/3
这样的函数也将对手名称
作为输入,我决定将其删除,因此我将new_game/2更改为new_game/1
并在gameclient
中将make_move/3更改为make_move/2
,如下:
-module(testing).
%% ====================================================================
%% API functions
%% ====================================================================
-export([all/0]).
%% ====================================================================
%% Internal functions
%% ====================================================================
all()->
test1(),
timer:sleep(75),
test2(),
timer:sleep(75),
test3(),
timer:sleep(75).
test1()->
G_serv_Output1=gameserver:start(),
io:format("Value of G_serv_Output in test1 is ~p~n",
[G_serv_Output1]),
Abhishek=gameclient:login("Abhishek"),
Dharun=gameclient:login("Dharun"),
gameclient:new_game(Abhishek,"Dharun"),
timer:sleep(5),
gameclient:make_move(Abhishek,"Dharun", a1),
timer:sleep(5),
gameclient:make_move(Dharun,"Abhishek", b2),
timer:sleep(5),
gameclient:make_move(Abhishek,"Dharun", a2),
timer:sleep(5),
gameclient:make_move(Dharun,"Abhishek", c3),
timer:sleep(5),
gameclient:make_move(Abhishek,"Dharun", a3).
test2()->
G_serv_Output1=gameserver:start(),
io:format("Value of G_serv_Output in test2 is ~p~n",
[G_serv_Output1]),
Abhijeet=gameclient:login("Abhijeet"),
Ranjan=gameclient:login("Ranjan"),
gameclient:new_game(Abhijeet,"Ranjan"),
timer:sleep(5),
gameclient:make_move(Abhijeet,"Ranjan", a1),
timer:sleep(5),
gameclient:make_move(Ranjan,"Abhijeet", b2),
timer:sleep(5),
gameclient:make_move(Abhijeet,"Ranjan", a2),
timer:sleep(5),
gameclient:make_move(Ranjan,"Abhijeet", c3),
timer:sleep(5),
gameclient:make_move(Abhijeet,"Ranjan", a3).
test3()->
G_serv_Output1=gameserver:start(),
io:format("Value of G_serv_Output in test3 is ~p~n",
[G_serv_Output1]),
Bana=gameclient:login("Bana"),
Sagar=gameclient:login("Sagar"),
gameclient:new_game(Bana,"Sagar"),
timer:sleep(5),
gameclient:make_move(Bana,"Sagar", a1),
timer:sleep(5),
gameclient:make_move(Sagar,"Bana", b2),
timer:sleep(5),
gameclient:make_move(Bana,"Sagar", a2),
timer:sleep(5),
gameclient:make_move(Sagar,"Bana", c3),
timer:sleep(5),
gameclient:make_move(Bana,"Sagar", a3).
new_game(Pid) ->
Pid ! {new_game}.
make_move(Pid, Move) ->
Pid ! { make_move, Move }.
loop(Name) ->
receive
{ new_game} ->
gameserver:new_game(Name),
loop(Name);
{ make_move, Move } ->
gameserver:make_move(Name, Move),
loop(Name)
end.
-module(testing2).
-include_lib("common_test/include/ct.hrl").
%% ====================================================================
%% API functions
%% ====================================================================
-export([all/0, groups/0, init_per_group/2, end_per_group/2]).
-export([test1/1,test2/1]).
%% ====================================================================
%% Internal functions
%% ====================================================================
all() -> [{group, session}].
groups() -> [{session,
[],
[{group, clients}]},
{clients,
[parallel, {repeat, 1}],
[test1, test2]}].
init_per_group(session, Config) ->
gameserver:start(),
Config;
init_per_group(_, Config) ->
Config.
end_per_group(session, _Config) ->
gameserver:stop();
end_per_group(_, _Config) ->
ok.
test1(_Config)->
Abhishek=gameclient:login("Abhishek"),
Dharun=gameclient:login("Dharun"),
gameclient:new_game(Abhishek),
timer:sleep(10),
gameclient:make_move(Abhishek, a1),
timer:sleep(10),
gameclient:make_move(Dharun, b2),
timer:sleep(10),
gameclient:make_move(Abhishek, a2),
timer:sleep(10),
gameclient:make_move(Dharun, c3),
timer:sleep(10),
gameclient:make_move(Abhishek, a3).
test2(_Config)->
Abhijeet=gameclient:login("Abhijeet"),
Ranjan=gameclient:login("Ranjan"),
gameclient:new_game(Abhijeet),
timer:sleep(10),
gameclient:make_move(Abhijeet, a1),
timer:sleep(10),
gameclient:make_move(Ranjan, b2),
timer:sleep(10),
gameclient:make_move(Abhijeet, a2),
timer:sleep(10),
gameclient:make_move(Ranjan, c3),
timer:sleep(10),
gameclient:make_move(Abhijeet, a3).
在gameserver
模块中:
new_game(Name) ->
global:send(?SERVER, { new_game, Name}).
make_move(Name, Move) ->
global:send(?SERVER, { make_move, Name, Move }).
game_loop(Players, Games) ->
process_flag(trap_exit, true),
receive
{ new_game, Name} ->
PlayerList=dict:fetch_keys(Players),
PlayerAndOpponent=lists:partition(fun(A)->Name==A end, PlayerList),
{_,OpponentNameList}=PlayerAndOpponent,
OpponentName=list_to_tuple(OpponentNameList),
case dict:find(Name, Players) of
{ ok, Pid } ->
case dict:find(element(1,OpponentName), Players) of
{ ok, OpponentPid} ->
Pid ! { msg, "Ready to rumble against " ++ element(1,OpponentName) ++ "!" },
OpponentPid ! { msg, "Ready to rumble against " ++ Name ++ "!" },
GamePid = tictactoe:start({Pid, Name}, {OpponentPid, element(1,OpponentName)}),
GameKey = create_game_key(Name, element(1,OpponentName)),
link(GamePid),
game_loop(Players, dict:store(GameKey, GamePid, Games));
error ->
Pid ! { msg, "Did not find opponent " ++ OpponentName ++ "!" },
game_loop(Players, Games)
end;
error ->
io:format("Could not find player ~p~n", [ Name ]),
game_loop(Players, Games)
end;
{ make_move, Name, Move } ->
PlayerList=dict:fetch_keys(Players),
PlayerAndOpponent=lists:partition(fun(A)->Name==A end, PlayerList),
{_,OpponentNameList}=PlayerAndOpponent,
OpponentName=list_to_tuple(OpponentNameList),
case dict:find(create_game_key(Name, element(1,OpponentName)), Games) of
{ ok, GamePid } ->
tictactoe:make_move(GamePid, Name, Move),
game_loop(Players, Games);
error ->
game_loop(Players, Games)
end
end.
保留两个模块中的其余代码不变
但是现在当我运行模块时,我发现只有test1成功运行,而test2和test3没有成功运行我试图找到原因,我发现当test2和test3启动时,test1中的玩家名称仍然在dict中,但由于我在所有三个测试中都调用了gameserver:start/0
,所以不应该发生这种情况
问题:
如果我必须为两个用户测试TictaToe游戏,但他们必须同时玩,那么我的测试用例模块应该是什么样子
编辑后
我研究了erlang的common_测试库,并在同一个问题上实现了它
通过创建一个新模块testing2
,如下所示:
-module(testing).
%% ====================================================================
%% API functions
%% ====================================================================
-export([all/0]).
%% ====================================================================
%% Internal functions
%% ====================================================================
all()->
test1(),
timer:sleep(75),
test2(),
timer:sleep(75),
test3(),
timer:sleep(75).
test1()->
G_serv_Output1=gameserver:start(),
io:format("Value of G_serv_Output in test1 is ~p~n",
[G_serv_Output1]),
Abhishek=gameclient:login("Abhishek"),
Dharun=gameclient:login("Dharun"),
gameclient:new_game(Abhishek,"Dharun"),
timer:sleep(5),
gameclient:make_move(Abhishek,"Dharun", a1),
timer:sleep(5),
gameclient:make_move(Dharun,"Abhishek", b2),
timer:sleep(5),
gameclient:make_move(Abhishek,"Dharun", a2),
timer:sleep(5),
gameclient:make_move(Dharun,"Abhishek", c3),
timer:sleep(5),
gameclient:make_move(Abhishek,"Dharun", a3).
test2()->
G_serv_Output1=gameserver:start(),
io:format("Value of G_serv_Output in test2 is ~p~n",
[G_serv_Output1]),
Abhijeet=gameclient:login("Abhijeet"),
Ranjan=gameclient:login("Ranjan"),
gameclient:new_game(Abhijeet,"Ranjan"),
timer:sleep(5),
gameclient:make_move(Abhijeet,"Ranjan", a1),
timer:sleep(5),
gameclient:make_move(Ranjan,"Abhijeet", b2),
timer:sleep(5),
gameclient:make_move(Abhijeet,"Ranjan", a2),
timer:sleep(5),
gameclient:make_move(Ranjan,"Abhijeet", c3),
timer:sleep(5),
gameclient:make_move(Abhijeet,"Ranjan", a3).
test3()->
G_serv_Output1=gameserver:start(),
io:format("Value of G_serv_Output in test3 is ~p~n",
[G_serv_Output1]),
Bana=gameclient:login("Bana"),
Sagar=gameclient:login("Sagar"),
gameclient:new_game(Bana,"Sagar"),
timer:sleep(5),
gameclient:make_move(Bana,"Sagar", a1),
timer:sleep(5),
gameclient:make_move(Sagar,"Bana", b2),
timer:sleep(5),
gameclient:make_move(Bana,"Sagar", a2),
timer:sleep(5),
gameclient:make_move(Sagar,"Bana", c3),
timer:sleep(5),
gameclient:make_move(Bana,"Sagar", a3).
new_game(Pid) ->
Pid ! {new_game}.
make_move(Pid, Move) ->
Pid ! { make_move, Move }.
loop(Name) ->
receive
{ new_game} ->
gameserver:new_game(Name),
loop(Name);
{ make_move, Move } ->
gameserver:make_move(Name, Move),
loop(Name)
end.
-module(testing2).
-include_lib("common_test/include/ct.hrl").
%% ====================================================================
%% API functions
%% ====================================================================
-export([all/0, groups/0, init_per_group/2, end_per_group/2]).
-export([test1/1,test2/1]).
%% ====================================================================
%% Internal functions
%% ====================================================================
all() -> [{group, session}].
groups() -> [{session,
[],
[{group, clients}]},
{clients,
[parallel, {repeat, 1}],
[test1, test2]}].
init_per_group(session, Config) ->
gameserver:start(),
Config;
init_per_group(_, Config) ->
Config.
end_per_group(session, _Config) ->
gameserver:stop();
end_per_group(_, _Config) ->
ok.
test1(_Config)->
Abhishek=gameclient:login("Abhishek"),
Dharun=gameclient:login("Dharun"),
gameclient:new_game(Abhishek),
timer:sleep(10),
gameclient:make_move(Abhishek, a1),
timer:sleep(10),
gameclient:make_move(Dharun, b2),
timer:sleep(10),
gameclient:make_move(Abhishek, a2),
timer:sleep(10),
gameclient:make_move(Dharun, c3),
timer:sleep(10),
gameclient:make_move(Abhishek, a3).
test2(_Config)->
Abhijeet=gameclient:login("Abhijeet"),
Ranjan=gameclient:login("Ranjan"),
gameclient:new_game(Abhijeet),
timer:sleep(10),
gameclient:make_move(Abhijeet, a1),
timer:sleep(10),
gameclient:make_move(Ranjan, b2),
timer:sleep(10),
gameclient:make_move(Abhijeet, a2),
timer:sleep(10),
gameclient:make_move(Ranjan, c3),
timer:sleep(10),
gameclient:make_move(Abhijeet, a3).
但是当我看到ct生成的html日志时,我仍然看不到这些游戏正在运行,而是看到了以下内容
在测试1中
=== Started at 2017-09-19 11:48:08
"Welcome to tictactoe server!"
"Welcome to tictactoe server!"
"Ready to rumble against Abhishek!"
"Ready to rumble against Dharun!"
=== Ended at 2017-09-19 11:48:08
=== successfully completed test case
=== Returned value: {make_move,a3}
和在测试2中
=== Started at 2017-09-19 11:48:08
"Welcome to tictactoe server!"
"Welcome to tictactoe server!"
"Ready to rumble against Ranjan!"
"Ready to rumble against Abhijeet!"
"It is not your turn yet!"
"It is not your turn yet!"
=== Ended at 2017-09-19 11:48:08
=== successfully completed test case
=== Returned value: {make_move,a3}
请帮帮我。我对测试的要求非常严格。你可以有意义地测试纯功能,也可以有意义地对集成系统进行外部测试,但在这两者之间,是一堆模糊的疯狂,似乎没有人能比现实世界中的用户测试更好地处理(尽管许多宗教和意识形态都是围绕这一点出现的)。也就是说,我相信最好的利用时间的方法是先了解基于属性的测试,然后再了解基于属性的测试。其他测试方法和框架可以等待。@zxq9感谢您的链接。那么这是否意味着项目(这个游戏)是如何进行的呢当多个用户在玩时,它的行为只有在现实世界中有多对人在玩时才能被检查。我们不能通过编写一个或两个模块来检查它吗?不幸的是,并发性并没有那么简单。在进程之间编写的所有内容最多都具有部分事件顺序。因此,系统状态是不可证明的。Erlang的“让它崩溃”和严格的进程隔离是针对管理这一难题而设计的。并发系统极其微妙和复杂,因此当我们遇到意外情况时,我们会崩溃。你的测试模型只会和你的分析一样可靠(你会错过一些东西)。如果您考虑将其用于测试,那么您已经编写了模块来处理它。例外是纯函数。实际上我对erlang是新手,我的目标是了解erlang中的测试是如何进行的。因此,如果我有一些用erlang编写的项目,1)我应该只在单元方面检查它。2) 那么,我如何才能真正测试该项目的并发性呢?@abhishekranjan您提供了一个指向代码的链接,这很好,但如果您在那里有一个分支,并对问题中所述的更改,那就更好了。这样可以更容易地找到代码中不起作用的地方。我对测试的态度非常严格。你可以有意义地测试纯功能,也可以有意义地对集成系统进行外部测试,但在这两者之间,是一堆模糊的疯狂,似乎没有人能比现实世界中的用户测试更好地处理(尽管许多宗教和意识形态都是围绕这一点出现的)。也就是说,我相信最好的利用时间的方法是先了解基于属性的测试,然后再了解基于属性的测试。其他测试方法和框架可以等待。@zxq9感谢您的链接。那么这是否意味着项目(这个游戏)是如何进行的呢当多个用户在玩时,它的行为只有在现实世界中有多对人在玩时才能被检查。我们不能通过编写一个或两个模块来检查它吗?不幸的是,并发性并没有那么简单。在进程之间编写的所有内容最多都具有部分事件顺序。因此,系统状态是不可证明的。Erlang的“让它崩溃”和严格的进程隔离是针对管理这一难题而设计的。并发系统极其微妙和复杂,因此当我们遇到意外情况时,我们会崩溃。你的测试模型只会和你的分析一样可靠(你会错过一些东西)。如果您考虑将其用于测试,那么您已经编写了模块来处理它。例外是纯函数。实际上我对erlang是新手,我的目标是了解erlang中的测试是如何进行的。因此,如果我有一些用erlang编写的项目,1)我应该只在单元方面检查它。2) 那么,我如何才能真正测试该项目的并发性呢?@abhishekranjan您提供了一个指向代码的链接,这很好,但如果您在那里有一个分支,并对问题中所述的更改,那就更好了。这样可以更容易地找到代码中不起作用的内容。