NGRX测试:减速器
了解如何使用Jest进行测试NGRX减速器。
目标是由于调度行动而将突变视为申请状态。️️
下载
在这篇文章中,我将使用演示申请。您可以克隆存储库或下载源代码的zip文件:
系列
此帖子是使用JEST测试NGRX的系列的一部分:
栈
此应用程序的堆栈将是:
- 角6
- NGRX 6.
- Jest和Angular-Jest-Preset模块
- 茉莉花大理石模块
- 创建假数据的煽动者
Jest Test Runner.
正如我上面提到的那样,这篇文章将使用Jest测试赛道。它比Karma更快(即使是无头铬),也可以使用茉莉花。事实上,如果您的大部分测试由CLI生成,那么您可以简单地为jest交换业力。
如果你是新人,用角度看看我的帖子。
诺夫动作
首先,我们应该断言国家是不突变为无效或诺沃行动减速器()
功能在src / app / state / user / user.reducer.spec.ts:
描述('未定义的行动'那()=>{它('应该退回默认状态'那()=>{const行动={类型:'诺普'}如任何;const结果=减速器(未定义那行动);期望(结果)。成为(Initimstate.);});});
让我们查看上面的测试:
- 在“应该返回默认状态”规范中,我们创建一个新的
行动
,谁的类型
被设置为“noop”。 - 然后我们援引
减速器()
功能指定未定义
对于当前州
和诺普一起行动
。 - 最后,我们
期望
结果状态与Initimstate.
,这是默认值州
争论在减速器()
功能。
如果我们运行我们的测试,它应该通过:
$NPM.测试
添加用户
行动
在示例应用程序中,我为添加用户指定了一些操作SRC / app /州/用户/ user.actions.ts:
出口枚举UserActiontypes.{添加用户='[用户]添加用户'那addusersuccess.='[用户]添加用户成功'那adduserfail.='[用户]添加用户失败'那}
在里面src / app /用户/ containers / add / add.component.ts组成部分调度()
该添加用户
行动。这将切换装载
布尔属性在州,和添加用户
效果src / app / store / user / user.effects.ts将举行援助userservice.adduser()
方法,它将向我们的API发送一个请求以创建新的用户实体。请求完成后,我们会发出addusersuccess.
行动,或者如果响应是错误(例如500个状态代码),我们会发出adduserfail.
行动。
这是一个定义添加用户
行动:
出口类添加用户实施行动{readonly类型=UserActiontypes.。添加用户;构造函数(上市有效载荷:{用户:偏<用户>}){}}
为了有效载荷
我们只是定义一个必须具有的对象用户
财产,这是一个偏
A.用户
。
接下来,让我们来看看减速器()
执行此操作:
出口功能减速器(州=Initimstate.那行动:用户名):州{开关(行动。类型){案件UserActiontypes.。添加用户:返回{......州那装载:真正那错误:未定义};}}
当。。。的时候添加用户
派遣行动我们正在更新状态,以便装载
布尔值设置为真正
并确保了错误
属性设置为未定义
。
这是测试添加用户
行动在src / app / state / user / user.reducer.spec.ts文件:
描述('用户减速器'那()=>{描述('[用户]添加用户'那()=>{它('应该切换加载状态'那()=>{const行动=新添加用户({用户});const结果=减速器(Initimstate.那行动);期望(结果)。toequal.({......Initimstate.那错误:未定义那装载:真正});});});});
让我们来查看测试:
- 首先,我们的所有测试都在“用户减速器”测试套件内。
- 在测试中,我们新起来
添加用户
操作,指定我们使用Faker创建的生成用户。 - 然后我们援引
减速器()
功能,提供Initimstate.
和行动
。 - 最后,我们
期望
该结果
等于一个新对象;首先传播Initimstate.
对象,然后指定错误
和装载
属性值。
addusersuccess.
行动
接下来,让我们断言addusersuccess.
操作返回新创建的新状态用户
实体添加。
在此演示应用程序中,我正在使用@ ngrx /实体模块来管理集合用户
对象。虽然可能没有必要断言单独使用实体适配器的状态突变,但我想明确测试每个操作,包括那些单独/主要使用适配器方法的操作(例如,附加()
)对于突变申请的状态。
一般来说,我认为这是安全的:
假设@ ngrx /实体适配器状态突变是经过战斗的,可能在您的应用程序中不需要测试
首先,这是一个定义addusersuccess.
行动SRC / app /州/用户/ user.actions.ts:
出口类addusersuccess.实施行动{readonly类型=UserActiontypes.。addusersuccess.;构造函数(上市有效载荷:{用户:用户}){}}
而且,这里是案例陈述减速器()
为了addusersuccess.
返回新州的行动:
案件UserActiontypes.。addusersuccess.:案件UserActiontypes.。LoadUserSuccess.:{返回适配器。adjone.(行动。有效载荷。用户那{......州那装载:假});}
正如你所看到的,我们调用附加()
在实体适配器上的方法,提供创建(或加载)和切换的用户装载
布尔属性到假
。
现在,让我们看看测试中的测试src / app / state / user / user.reducer.spec.ts文件:
描述('[用户]添加用户成功'那()=>{它('应该将用户添加到州'那()=>{const行动=新addusersuccess.({用户});const结果=减速器(Initimstate.那行动);期望(结果)。toequal.({......Initimstate.那实体:{[用户。ID]:用户}那ids.:[用户。ID]那装载:假});});});
在这个测试中,我们调用了减速器()
功能,指定Initimstate.
随着addusersuccess.
我们创建了必要的有效载荷的行动。然后我们期望()
结果等于Initimstate.
额外的东西实体
和ids.
通过适配器创建的属性附加()
方法。
我们也断言了装载
布尔属性已设置为假
。这实际上是在减速机中执行的唯一“手动”状态突变值得被测试。
adduserfail.
行动
我们还应在添加新用户的HTTP请求失败时测试失败路径。为此,我们会宣传那个adduserfail.
行动导致我们申请的新状态错误
属性包含错误
发生了。
首先,这是一个定义adduserfail.
行动SRC / app /州/用户/ user.actions.ts:
出口类adduserfail.实施行动{readonly类型=UserActiontypes.。adduserfail.;构造函数(上市有效载荷:{错误:错误}){}}
而且,这里是案例陈述减速器()
功能:
案件UserActiontypes.。adduserfail.:案件UserActiontypes.。addusersfail.:案件UserActiontypes.。LoadUserFail.:案件UserActiontypes.。LoadUsersfail.:案件UserActiontypes.。updateUserfail.:案件UserActiontypes.。UpdateUsersfail.:{返回{......州那错误:行动。有效载荷。错误那装载:假};}
您将注意到,在此应用程序中,当任何操作失败时,我们具有相同的结果:我们更新错误
财产包含错误
这是在行动中派出的有效载荷
并切换装载
布尔到假
。
现在,让我们断言,该状态在适当的时候突变adduserfail.
行动被派遣:
描述('[用户]添加用户失败'那()=>{它('应该在状态下更新错误'那()=>{const错误=新错误();const行动=新adduserfail.({错误});const结果=减速器(Initimstate.那行动);期望(结果)。toequal.({......Initimstate.那错误那装载:假});});});
这应该开始看起来很害怕。我们加入了adduserfail.
行动,援引减速器()
功能,并断言结果
等于申请的预期状态。
LoadUser.
那LoadUserSuccess.
和LoadUserFail.
测试的测试LoadUser.
那LoadUserSuccess.
和LoadUserFail.
几乎与添加用户的人数几乎没有专制。让我们快速看看每个人。
断言这一点LoadUser.
行动结果
等于预期的价值错误
属性是未定义
和装载
财产被切断到真正
:
描述('[用户]加载用户'那()=>{它('应该切换加载状态'那()=>{const行动=新LoadUser.({ID:用户。ID});const结果=减速器(Initimstate.那行动);期望(结果)。toequal.({......Initimstate.那错误:未定义那装载:真正});});});
断言这一点LoadUserSuccess.
行动结果
等于预期的价值实体
和ids.
属性包含在操作中发送的用户有效载荷
:
描述('[用户]加载用户成功'那()=>{它('应该将用户加载到州'那()=>{const行动=新LoadUserSuccess.({用户});const结果=减速器(Initimstate.那行动);期望(结果)。toequal.({......Initimstate.那实体:{[用户。ID]:用户}那ids.:[用户。ID]那装载:假});});});
断言这一点LoadUserFail.
行动结果
等于预期的价值错误
属性包含错误
这是在行动中派出的有效载荷
:
描述('[用户]加载用户失败'那()=>{它('应该在状态下更新错误'那()=>{const错误=新错误();const行动=新LoadUserFail.({错误});const结果=减速器(Initimstate.那行动);期望(结果)。toequal.({......Initimstate.那错误那装载:假});});});
LoadUsers.
那LoadUsersSuccess.
和LoadUsersfail.
单位测试LoadUsers.
那LoadUsersSuccess.
和LoadUsersfail.
行动是相似的,除了实体
和ids.
属性已更新许多用户
对象。
我们会跳过这一点LoadUsers.
和LoadUsersfail.
为了简洁的测试,因为它们几乎与之相同LoadUser.
和LoadUserFail.
测试。
让我们快点看看测试LoadUsersSuccess.
行动:
描述('[用户]负载用户成功'那()=>{它('应该将所有用户添加到州'那()=>{const用户=[用户];const行动=新LoadUsersSuccess.({用户});const结果=减速器(Initimstate.那行动);期望(结果)。toequal.({......Initimstate.那实体:用户。降低((entityMap.那用户)=>({......entityMap.那[用户。ID]:用户})那{})那ids.:用户。地图(用户=>用户。ID)那装载:假});});});
在里面期望()
断言我们使用array.prototype.reduce()
创建的方法实体
字典的用户
对象。
@ ngrx / Entity库使用以下签名来存储实体
在状态以便快速检索对象:
出口声明键入diblditnnum.<T.>={[ID:数]:T.;};出口摘要类字典<T.>实施intermennum.<T.>{[ID:串]:T.;}
唯一标识符是一个数
或者串
,唯一标识符是属性名称(或密钥)实体
目的。
这就是我们使用的原因降低()
创建的方法实体
匹配的对象intermennum.
要么字典
类型。
该ids.
数组只是一个数组ID
价值观。我们使用地图()
映射值的方法用户
数组用户
对象。
UpdateUserSuccess.
测试的测试更新用户
和updateUserfail.
行动也与我们查看的现有测试相同,我们正在调度切换的动作装载
财产并更新错误
属性。
另一方面,接通一个断言的测试UpdateUserSuccess.
动作突变我们的应用程序的状态有点复杂,并且坦率地说,有点冗长:
描述('用户减速器'那()=>{const用户:用户={ID:1那名字:'Anakin'那姓:'天空步行者'};描述('[用户]更新用户成功'那()=>{它('应该在州更新用户'那()=>{constUpdatedUser.:用户={......用户那名字:'Darth'那姓:'vader'};const行动=新UpdateUserSuccess.({更新:{ID:用户。ID那变化:UpdatedUser.}});const州=减速器(Initimstate.那新addusersuccess.({用户}));期望(州)。toequal.({......Initimstate.那实体:{[用户。ID]:用户}那ids.:[用户。ID]那装载:假});const结果=减速器(州那行动);期望(结果)。toequal.({......州那实体:{......州。实体那[用户。ID]:UpdatedUser.}那ids.:[......州。ids.]那装载:假});});});});
这是什么UpdateUserSuccess.
测试在做:
- 首先,我们定义一个
UpdatedUser.
使用当前的对象用户
对象属性修改名字
和姓
属性值。我们在这里去了一场星球大战主题。如果您反对星球大战,请与其一起进行此测试。如果你是一个粉丝,希望你能享受乐趣,我们将为我们的角度应用编写单元测试。 - 然后我们新起来
UpdateUserSuccess.
行动与之更新
属性。再次,我们正在使用@ ngrx /实体库,所以我们指定了一个更新
类型签名ID
和变化
属性。 - 我们举行了
减速器()
提供的功能Initimstate.
和一个新的addusersuccess.
行动。 - 我们断言那个
addusersuccess.
行动适当地返回一个新的州
添加新用户的对象。 - 然后,我们调动
减速器()
再次函数。这次我们提供了州
那是一个结果addusersuccess.
行动,以及行动
实例UpdateUserSuccess.
。 - 最后,我们断言了
结果
当UpdateUserSuccess.
行动代表了UpdatedUser.
。
UpdateUssSuccess.
测试的测试UpdateUssSuccess.
行动类似于我们刚看过的测试UpdateUserSuccess.
随着更新多个用户的添加:
描述('用户减速器'那()=>{const用户:用户={ID:1那名字:'Anakin'那姓:'天空步行者'};描述('[用户]更新用户成功'那()=>{它('应该将所有用户添加到州'那()=>{const参议员={ID:2那名字:'sheev'那姓:'Palpaatine'};constVader.={......用户那名字:'Darth'那姓:'vader'};consts={......参议员那名字:'Darth'那姓:“周围”};const最初的用户=[用户那参议员];const更新者=[Vader.那s];const州=减速器(Initimstate.那新addUssssuccess.({用户:最初的用户}));const行动=新UpdateUssSuccess.({更新:[{ID:用户。ID那变化:Vader.}那{ID:参议员。ID那变化:s}]});const结果=减速器(州那行动);期望(结果)。toequal.({......州那实体:更新者。降低((entityMap.那用户)=>({......entityMap.那[用户。ID]:用户})那{})那ids.:更新者。地图(用户=>用户。ID)那装载:假});});});});
我不会审查每行代码,但一般来说,我们要添加最初的用户
到我们的应用程序,然后断言,通过该申请更新多个用户UpdateUssSuccess.
行动导致预期的状态。
选择器
最后一次测试src / app / state / user / user.reducer.spec.ts是断言选择器
操作更新选择用户
我们的国家对象中的财产:
描述('[用户]选择用户'那()=>{它('应该在状态下设置SelectsUlerId属性那()=>{const行动=新选择器({ID:用户。ID});const结果=减速器(Initimstate.那行动);期望(结果)。toequal.({......Initimstate.那selectedUserid.:用户。ID});});});
运行测试
最后,我们使用测试:
$NPM.测试
我们应该看到我们所有的测试通过。
结论
写作单元测试能够玩得开心!☝️
没有真实地,可以轻松地完成使用NGRX的角度应用程序中的单元测试操作。这些例子中唯一有点繁琐写入写入的测试是更新测试,特别是一次更新多个实体。但是,我们现在具有优异的代码覆盖率☀️并确保我们的行动通过该行动适当地突变国家减速器()
功能。