首页 > web前端 > js教程 > AngularJS中的单元测试:服务,控制器和提供商

AngularJS中的单元测试:服务,控制器和提供商

Lisa Kudrow
发布: 2025-02-20 08:31:09
原创
852 人浏览过

AngularJS中的单元测试:服务,控制器和提供商

钥匙要点

  • angularJS的设计考虑了可检验性,使用依赖注入使单位测试更加容易,并允许将应用程序模块化为不同组件,例如服务,控制器和提供商。
  • > 控制器是不可注射的,而是在编译路线负载或NG-Controller指令时自动实例化。测试控制器时,需要手动实例化,并在汇编视图后添加到范围中的任何对象需要手动创建并添加到控制器中。> AngularJ中的>提供商用于公开API,以进行全应用程序配置,该配置必须在应用程序启动之前进行。要测试提供商,需要加载和准备好模块,需要模拟依赖项,并且需要将回调传递给模块块。
  • > 在AngularJS中进行的
  • 单元测试可能很棘手,但值得花时间,因为它可以确保应用程序的正确性。 Angularjs使使用框架编写的测试代码更加容易。
  • > 在本文中,我将分享有关AngularJS中测试服务,控制器和提供商的一组技巧。代码片段是使用茉莉花编写的,可以使用业力测试跑者运行。您可以从我们的GitHub Repo下载本文中使用的代码,您还将在其中找到有关运行测试的说明。
>

测试服务

服务是AngularJS应用程序中最常见的组件之一。它们提供了一种在中心位置定义可重复使用的逻辑的方法,因此不需要一遍又一遍地重复相同的逻辑。该服务的单身性质使得可以通过多个控制器,指令甚至其他服务共享相同的数据。

服务可以取决于一组其他服务来执行其任务。说,名为A的服务取决于服务B,C和D执行其任务。在测试服务a时,依赖项B,C和D必须用模拟替换。

>

>我们通常会模拟所有依赖项,除了某些公用事业服务(例如$ rootscope和$ parse)。我们使用茉莉花(Jasmine.createspy()将在测试中必须检查的方法(在茉莉花中称为间谍)中必须检查的方法,该间谍将返回全新的功能。>>>>

>让我们考虑以下服务:

此服务只有一种方法(Showdialog)。根据输入的值此方法收到的输入值,它将将其注入依赖项($ window或modalsvc)中的两个服务之一。

angular<span>.module('services', [])
</span>  <span>.service('sampleSvc', ['$window', 'modalSvc', function($<span>window, modalSvc</span>){
</span>    <span>this.showDialog = function(message<span>, title</span>){
</span>      <span>if(title){
</span>        modalSvc<span>.showModalDialog({
</span>          <span>title: title,
</span>          <span>message: message
</span>        <span>});
</span>      <span>} else {
</span>        $<span>window.alert(message);
</span>      <span>}
</span>    <span>};
</span>  <span>}]);</span>
登录后复制
登录后复制
登录后复制
>要测试SamplesVC,我们需要模拟两个因服务,加载包含我们服务的角模块并获取对所有对象的引用:>

>现在我们可以测试Showdialog方法的行为。我们可以为该方法编写的两个测试用例如下:

<span>var mockWindow, mockModalSvc, sampleSvcObj;
</span><span>beforeEach(function(){
</span>  <span>module(function($provide){
</span>    $provide<span>.service('$window', function(){
</span>      <span>this.alert= jasmine.createSpy('alert');
</span>    <span>});
</span>    $provide<span>.service('modalSvc', function(){
</span>      <span>this.showModalDialog = jasmine.createSpy('showModalDialog');
</span>    <span>});
</span>  <span>});
</span>  <span>module('services');
</span><span>});
</span>
<span>beforeEach(inject(function($<span>window, modalSvc, sampleSvc</span>){
</span>  mockWindow<span>=$window;
</span>  mockModalSvc<span>=modalSvc;
</span>  sampleSvcObj<span>=sampleSvc;
</span><span>}));</span>
登录后复制
登录后复制
登录后复制
>如果没有标题是参数,请调用警报。

>如果存在标题和消息参数,请调用showmodalialog

  • 以下片段显示以下测试:
  • 此方法没有很多逻辑可以测试,而典型的Web应用程序中的服务通常会包含很多功能。您可以使用本提示中演示的技术来嘲笑和获取对服务的参考。服务测试应涵盖编写服务时假定的所有可能场景。>
工厂和价值也可以使用相同的技术进行测试。

>

测试控制器
<span>it('should show alert when title is not passed into showDialog', function(){
</span>  <span>var message="Some message";
</span>  sampleSvcObj<span>.showDialog(message);
</span>
  <span>expect(mockWindow.alert).toHaveBeenCalledWith(message);
</span>  <span>expect(mockModalSvc.showModalDialog).not.toHaveBeenCalled();
</span><span>});
</span>
<span>it('should show modal when title is passed into showDialog', function(){
</span>  <span>var message="Some message";
</span>  <span>var title="Some title";
</span>  sampleSvcObj<span>.showDialog(message, title);
</span>
  <span>expect(mockModalSvc.showModalDialog).toHaveBeenCalledWith({
</span>    <span>message: message,
</span>    <span>title: title
</span>  <span>});
</span>  <span>expect(mockWindow.alert).not.toHaveBeenCalled();
</span><span>});</span>
登录后复制
登录后复制
登录后复制

测试控制器的设置过程与服务的设置过程完全不同。这是因为控制器不可注射,而是在路由加载或编译NG控制器指令时自动实例化。由于我们没有测试中的视图加载,因此我们需要手动对正在测试的控制器进行实例化。 由于控制器通常与视图相关,因此控制器中方法的行为取决于视图。同样,在汇编视图后,可能会将一些其他对象添加到范围中。其中最常见的示例之一是形式对象。为了使测试按预期工作,必须手动创建这些对象并将其添加到控制器中。

>

一个控制器可以是以下类型之一:

>
  • >用于$ scope
  • 的控制器 与控制器一起使用的控制器作为语法
如果您不确定差异,可以在此处阅读更多有关它的信息。无论哪种方式,我们都会讨论这两个案例。

>

>带有$ scope

的测试控制器

考虑以下控制器:

为了测试此控制器,我们需要通过传递$ scope对象和一个模拟的服务对象(dataSVC)来创建控制器的实例。由于该服务包含异步方法,我们需要使用我在上一篇文章中概述的模拟承诺技术进行模拟。
angular<span>.module('services', [])
</span>  <span>.service('sampleSvc', ['$window', 'modalSvc', function($<span>window, modalSvc</span>){
</span>    <span>this.showDialog = function(message<span>, title</span>){
</span>      <span>if(title){
</span>        modalSvc<span>.showModalDialog({
</span>          <span>title: title,
</span>          <span>message: message
</span>        <span>});
</span>      <span>} else {
</span>        $<span>window.alert(message);
</span>      <span>}
</span>    <span>};
</span>  <span>}]);</span>
登录后复制
登录后复制
登录后复制
>

以下摘要模拟DataSvc服务:>

然后,我们可以使用$ Rootscope为控制器创建一个新的范围。$新方法。创建控制器的实例后,我们在此新$范围上都有所有字段和方法。

当控制器向$范围添加字段和方法时,我们可以检查它们是否设置为正确的值,以及该方法是否具有正确的逻辑。上面的示例控制器添加了正则表达式以检查有效的数字。 让我们添加一个规格来测试正则表达式的行为:

<span>var mockWindow, mockModalSvc, sampleSvcObj;
</span><span>beforeEach(function(){
</span>  <span>module(function($provide){
</span>    $provide<span>.service('$window', function(){
</span>      <span>this.alert= jasmine.createSpy('alert');
</span>    <span>});
</span>    $provide<span>.service('modalSvc', function(){
</span>      <span>this.showModalDialog = jasmine.createSpy('showModalDialog');
</span>    <span>});
</span>  <span>});
</span>  <span>module('services');
</span><span>});
</span>
<span>beforeEach(inject(function($<span>window, modalSvc, sampleSvc</span>){
</span>  mockWindow<span>=$window;
</span>  mockModalSvc<span>=modalSvc;
</span>  sampleSvcObj<span>=sampleSvc;
</span><span>}));</span>
登录后复制
登录后复制
登录后复制

如果控制器初始化具有默认值的任何对象,我们可以在规格中检查其值。

<span>it('should show alert when title is not passed into showDialog', function(){
</span>  <span>var message="Some message";
</span>  sampleSvcObj<span>.showDialog(message);
</span>
  <span>expect(mockWindow.alert).toHaveBeenCalledWith(message);
</span>  <span>expect(mockModalSvc.showModalDialog).not.toHaveBeenCalled();
</span><span>});
</span>
<span>it('should show modal when title is passed into showDialog', function(){
</span>  <span>var message="Some message";
</span>  <span>var title="Some title";
</span>  sampleSvcObj<span>.showDialog(message, title);
</span>
  <span>expect(mockModalSvc.showModalDialog).toHaveBeenCalledWith({
</span>    <span>message: message,
</span>    <span>title: title
</span>  <span>});
</span>  <span>expect(mockWindow.alert).not.toHaveBeenCalled();
</span><span>});</span>
登录后复制
登录后复制
登录后复制
要测试Savedata方法,我们需要为BookDetails和BookForm对象设置一些值。这些对象将绑定到UI元素,因此在收集视图时会在运行时创建。如前所述,在调用Savedata方法之前,我们需要用一些值手动对它们进行初始化。

以下摘要测试此方法:
angular<span>.module('controllers',[])
</span>  <span>.controller('FirstController', ['$scope','dataSvc', function($scope<span>, dataSvc</span>) {
</span>    $scope<span>.saveData = function () {
</span>      dataSvc<span>.save($scope.bookDetails).then(function (result) {
</span>        $scope<span>.bookDetails = {};
</span>        $scope<span>.bookForm.$setPristine();
</span>      <span>});
</span>    <span>};
</span>
    $scope<span>.numberPattern = <span>/<span>^\d*$</span>/</span>;
</span>  <span>}]);</span>
登录后复制
登录后复制

>用“控制器”语法

测试控制器的测试控制器

>测试使用控制器作为语法的控制器比使用$范围测试该控制器要容易得多。在这种情况下,控制器的实例扮演模型的角色。因此,所有操作和对象都可以在此实例上可用。

>

考虑以下控制器:
<span>module(function($provide){
</span>  $provide<span>.factory('dataSvc', ['$q', function($q)
</span>    <span>function save(data){
</span>      <span>if(passPromise){
</span>        <span>return $q.when();
</span>      <span>} else {
</span>        <span>return $q.reject();
</span>      <span>}
</span>    <span>}
</span>    <span>return{
</span>      <span>save: save
</span>    <span>};
</span>  <span>}]);
</span><span>});</span>
登录后复制

调用此控制器的过程与前面讨论的过程相似。唯一的区别是,我们不需要创建$范围。

>

>将控制器中的所有成员和方法添加到本实例中,我们可以使用实例参考访问它们。

<span>beforeEach(inject(function($rootScope<span>, $controller, dataSvc</span>){
</span>  scope<span>=$rootScope.$new();
</span>  mockDataSvc<span>=dataSvc;
</span>  <span>spyOn(mockDataSvc,'save').andCallThrough();
</span>  firstController <span>= $controller('FirstController', {
</span>    <span>$scope: scope, 
</span>    <span>dataSvc: mockDataSvc
</span>  <span>});
</span><span>}));</span>
登录后复制
以下摘要测试添加到上面控制器的numberPattern字段:>

SAVEDATA方法的断言保持不变。这种方法的唯一区别在于我们将值初始化为书籍尾尾和书形对象的方式。

<span>it('should have assigned right pattern to numberPattern', function(){
</span>    <span>expect(scope.numberPattern).toBeDefined();
</span>    <span>expect(scope.numberPattern.test("100")).toBe(true);
</span>    <span>expect(scope.numberPattern.test("100aa")).toBe(false);
</span><span>});</span>
登录后复制
以下片段显示了规格:

angular<span>.module('services', [])
</span>  <span>.service('sampleSvc', ['$window', 'modalSvc', function($<span>window, modalSvc</span>){
</span>    <span>this.showDialog = function(message<span>, title</span>){
</span>      <span>if(title){
</span>        modalSvc<span>.showModalDialog({
</span>          <span>title: title,
</span>          <span>message: message
</span>        <span>});
</span>      <span>} else {
</span>        $<span>window.alert(message);
</span>      <span>}
</span>    <span>};
</span>  <span>}]);</span>
登录后复制
登录后复制
登录后复制

>测试提供商

>提供商用于公开API,以进行全应用程序范围的配置,该配置必须在应用程序启动之前进行。一旦AngularJS应用程序的配置阶段结束了,就不允许与提供商的交互。因此,只有在配置块或其他提供商块中访问提供商。我们无法使用注入块获得提供商实例,而是需要将回调传递到模块块。

>让我们考虑以下依赖常数(AppConstants)第二提供商(另一个提供商)的提供商:>

为了测试这一点,我们首先需要模拟依赖项。您可以在示例代码中查看如何执行此操作。
<span>var mockWindow, mockModalSvc, sampleSvcObj;
</span><span>beforeEach(function(){
</span>  <span>module(function($provide){
</span>    $provide<span>.service('$window', function(){
</span>      <span>this.alert= jasmine.createSpy('alert');
</span>    <span>});
</span>    $provide<span>.service('modalSvc', function(){
</span>      <span>this.showModalDialog = jasmine.createSpy('showModalDialog');
</span>    <span>});
</span>  <span>});
</span>  <span>module('services');
</span><span>});
</span>
<span>beforeEach(inject(function($<span>window, modalSvc, sampleSvc</span>){
</span>  mockWindow<span>=$window;
</span>  mockModalSvc<span>=modalSvc;
</span>  sampleSvcObj<span>=sampleSvc;
</span><span>}));</span>
登录后复制
登录后复制
登录后复制
> 在测试提供商之前,我们需要确保将模块加载并准备就绪。在测试中,将延迟模块的加载直到执行注入块或执行第一个测试。在几个项目中,我看到了一些测试,这些测试使用了一个空的第一个测试来加载模块。我不是这种方法的粉丝,因为该测试无能为力,并为您的测试总数增加了计数。相反,我使用一个空的注射块来加载模块。

>

以下摘要获取参考并加载模块:>

>现在我们有所有参考文献,我们可以调用提供商中定义的方法并测试:

<span>it('should show alert when title is not passed into showDialog', function(){
</span>  <span>var message="Some message";
</span>  sampleSvcObj<span>.showDialog(message);
</span>
  <span>expect(mockWindow.alert).toHaveBeenCalledWith(message);
</span>  <span>expect(mockModalSvc.showModalDialog).not.toHaveBeenCalled();
</span><span>});
</span>
<span>it('should show modal when title is passed into showDialog', function(){
</span>  <span>var message="Some message";
</span>  <span>var title="Some title";
</span>  sampleSvcObj<span>.showDialog(message, title);
</span>
  <span>expect(mockModalSvc.showModalDialog).toHaveBeenCalledWith({
</span>    <span>message: message,
</span>    <span>title: title
</span>  <span>});
</span>  <span>expect(mockWindow.alert).not.toHaveBeenCalled();
</span><span>});</span>
登录后复制
登录后复制
登录后复制
结论

>单元测试有时会变得棘手,但是值得在其上花费时间,因为它可以确保应用程序的正确性。 AngularJS使使用框架编写的代码更加容易。我希望本文为您提供足够的想法来扩展和增强应用程序中的测试。在以后的文章中,我们将继续研究如何测试您的代码的其他片段。>
angular<span>.module('controllers',[])
</span>  <span>.controller('FirstController', ['$scope','dataSvc', function($scope<span>, dataSvc</span>) {
</span>    $scope<span>.saveData = function () {
</span>      dataSvc<span>.save($scope.bookDetails).then(function (result) {
</span>        $scope<span>.bookDetails = {};
</span>        $scope<span>.bookForm.$setPristine();
</span>      <span>});
</span>    <span>};
</span>
    $scope<span>.numberPattern = <span>/<span>^\d*$</span>/</span>;
</span>  <span>}]);</span>
登录后复制
登录后复制
>单位测试AngularJS服务,控制器和提供商的常见问题(常见问题解答)

在AngularJS中单位测试的重要性是什么?它有助于隔离单个组件(例如服务,控制器和提供商)的功能。这样可以确保每个组件在将它们集成到较大的应用程序中之前按预期工作。单元测试可以帮助识别开发过程的早期错误,从而使其更容易且成本更低。它还有助于维持代码质量并提高应用程序的整体可靠性。

>如何为AngularJS设置测试环境?首先,您需要安装必要的测试工具,例如茉莉花和业力。 Jasmine是用于测试JavaScript代码的行为驱动的开发框架,而Karma是在实际浏览器中执行测试的测试跑步者。安装这些工具后,您可以在单独的测试文件中编写测试用例并使用业力运行。

如何测试AngularJS服务?

>

>如何测试gangularjs控制器?

>测试AngularJS控制器涉及创建实例控制器并测试其方法和属性。您可以使用AngularJS提供的$控制器服务创建控制器的实例。创建实例后,您可以调用其方法并检查其对控制器范围的影响。您还可以通过嘲笑服务并验证其方法的调用来测试控制器与服务的互动。

>如何测试AngularJS提供商?

>测试AngularJS提供商类似于测试服务。您可以将提供商注入测试,模拟其依赖项并测试其方法。但是,提供者有一种称为“ $ get”的特殊方法,它返回服务实例。可以通过调用并检查返回的值来分别测试此方法。

依赖项注入是AngularJS的关键功能,使您可以注入依赖性成分。在测试中,您可以使用此功能将依赖项的模拟版本注入正在测试的组件中。这使您可以隔离组件并独立于其依赖项进行测试。

>

在单元测试angularJs组件中有哪些共同挑战?

>

>单位测试AngularJS组件中的一些共同挑战包括处理异步操作,测试指令以及处理外部依赖关系。异步操作可以使测试复杂且难以管理。由于与DOM的相互作用,测试指令可能很困难。外部依赖性(例如服务和API)可以使测试无法预测且难以隔离。

>

>如何在AngularJS测试中处理异步操作?

>

>在AngularJS测试中的异步操作可以使用$ Q服务以及茉莉花提供的“完成”函数来处理。 $ Q服务使您可以创建可以在测试中解决或拒绝的承诺。可以调用“完成”函数以表明异步操作已经完成。

>

>如何测试AngularJS指令?

>

>测试AngularJS指令涉及创建指令实例并测试其行为的实例。您可以使用AngularJS提供的$编译服务创建指令实例。创建实例后,您可以使用类似jQuery的方法对其进行操作,并检查其对范围的影响。

>

>我如何处理AngularJS测试中的外部依赖关系?可以通过嘲笑他们来处理。您可以创建依赖项的模拟版本,并将其注入正在测试的组件中。这使您可以控制依赖关系的行为,并隔离用于测试的组件。

>

以上是AngularJS中的单元测试:服务,控制器和提供商的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板