Angular js单元测试模拟文档

 tt 发布于 2023-02-13 15:55

我正在尝试测试角度服务,它通过$documentjasmine服务对DOM进行一些操作.假设它只是向元素添加了一些指令.

这样的服务看起来像

(function(module) {
    module.service('myService', [
        '$document',
        function($document) {
            this.doTheJob = function() {
                $document.find('body').append('');
            };
        }
    ]);
})(angular.module('my-app'));

我想像这样测试它

describe('Sample test' function() {
    var myService;

    var mockDoc;

    beforeEach(function() {
        module('my-app');

        // Initialize mock somehow. Below won't work indeed, it just shows the intent
        mockDoc = angular.element('');

        module(function($provide) {
            $provide.value('$document', mockDoc);
        });
    });

    beforeEach(inject(function(_myService_) {
        myService = _myService_;
    }));

    it('should append my-directive to body element', function() {
        myService.doTheJob();
        // Check mock's body to contain target directive
        expect(mockDoc.find('body').html()).toContain('');
    });
});

那么问题是创建这样的模拟的最佳方法是什么?

使用real进行测试document会给我们在每次测试后清理带来很多麻烦,并且看起来不像是一种方法.

我还尝试在每次测试之前创建一个新的真实文档实例,但结果却出现了不同的失败.

创建一个像下面这样的对象并检查whatever变量是否有效但看起来非常难看

var whatever = [];
var fakeDoc = {
    find: function(tag) {
              if (tag == 'body') {
                  return function() {
                      var self = this;
                      this.append = function(content) {
                          whatever.add(content);
                          return self;
                      };
                  };
              } 
          }
}

我觉得我在这里缺少一些重要的东西而且做错了.

任何帮助深表感谢.

2 个回答
  • 您可以使用DOMImplementation#createHTMLDocument()以下命令创建空测试文档:

    describe('myService', function() {
      var $body;
    
      beforeEach(function() {
        var doc;
    
        // Create an empty test document based on the current document.
        doc = document.implementation.createHTMLDocument();
    
        // Save a reference to the test document's body, for asserting
        // changes to it in our tests.
        $body = $(doc.body);
    
        // Load our app module and a custom, anonymous module.
        module('myApp', function($provide) {
          // Declare that this anonymous module provides a service
          // called $document that will supersede the built-in $document
          // service, injecting our empty test document instead.
          $provide.value('$document', $(doc));
        });
    
        // ...
      });
    
      // ...
    });
    

    因为您要为每个测试创建一个新的空文档,所以不会干扰运行测试的页面,并且在测试之间的服务之后不必显式清理.

    2023-02-13 15:57 回答
  • $document在这种情况下,您无需模拟服务.使用它的实际实现更容易:

    describe('Sample test', function() {
        var myService;
        var $document;
    
        beforeEach(function() {
            module('plunker');
        });
    
        beforeEach(inject(function(_myService_, _$document_) {
            myService = _myService_;
            $document = _$document_;
        }));
    
        it('should append my-directive to body element', function() {
            myService.doTheJob();
            expect($document.find('body').html()).toContain('<my-directive></my-directive>');
        });
    });
    

    这里的人物.

    如果你真的需要嘲笑它,那么我想你必须按照你的方式去做:

    $documentMock = { ... }
    

    但这可能会破坏依赖于$document服务本身的其他事物(例如,使用这种指令createElement).

    UPDATE

    如果您需要在每次测试后将文档恢复到一致状态,则可以按以下方式执行操作:

    afterEach(function() {
        $document.find('body').html(''); // or $document.find('body').empty()
                                         // if jQuery is available
    });
    

    Plunker 在这里(我必须使用另一个容器,否则Jasmine结果将不会被渲染).

    正如@AlexanderNyrkov在评论中指出的那样,Jasmine和Karma都在body标签内部有自己的东西,并且通过清空文档主体来擦除它们似乎不是一个好主意.

    更新2

    我设法部分模拟了$document服务,因此您可以使用实际的页面文档并将所有内容恢复到有效状态:

    beforeEach(function() {
        module('plunker');
    
        $document = angular.element(document); // This is exactly what Angular does
        $document.find('body').append('<content></content>');
    
        var originalFind = $document.find;
        $document.find = function(selector) {
          if (selector === 'body') {
            return originalFind.call($document, 'body').find('content');
          } else {
            return originalFind.call($document, selector);
          }
        }
    
        module(function($provide) {
          $provide.value('$document', $document);
        });        
    });
    
    afterEach(function() {
        $document.find('body').html('');
    });
    

    这里的人物.

    我们的想法是body用你的SUT可以自由操作的新标签替换标签,并且你的测试可以在每个规范的最后安全地清除.

    2023-02-13 15:57 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有