Angular js 单元测试模拟文档 [英] Angular js unit test mock document

查看:25
本文介绍了Angular js 单元测试模拟文档的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 jasmine 测试通过 $document 服务对 DOM 进行一些操作的 angular 服务.假设它只是将一些指令附加到 元素.

I am trying to test angular service which does some manipulations to DOM via $document service with jasmine. Let's say it simply appends some directive to the <body> element.

这样的服务可能看起来像

Such service could look like

(function(module) {
    module.service('myService', [
        '$document',
        function($document) {
            this.doTheJob = function() {
                $document.find('body').append('<my-directive></my directive>');
            };
        }
    ]);
})(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('<html><head></head><body></body></html>');

        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('<my-directive></my-directive>');
    });
});

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

So the question is what would be the best way to create such mock?

使用真实的document 进行测试会给我们在每次测试后清理工作带来很多麻烦,而且看起来不像是一种可行的方法.

Testing with real document will give us much trouble cleaning up after each test and does not look like a way to go with.

我还尝试在每次测试之前创建一个新的真实文档实例,但最终都以不同的失败告终.

I've also tried to create a new real document instance before each test, yet ended up with different failures.

创建一个像下面这样的对象并检查 whatever 变量有效但看起来很丑

Creating an object like below and checking whatever variable works but looks very ugly

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

我觉得我在这里遗漏了一些重要的东西并且做错了一些事情.

I feel that I'm missing something important here and doing something very wrong.

非常感谢任何帮助.

推荐答案

在这种情况下,您不需要模拟 $document 服务.使用它的实际实现更容易:

You don't need to mock the $document service in such a case. It's easier just to use its actual implementation:

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>');
    });
});

Plunker 这里.

如果你真的需要模拟它,那么我想你必须按照你的方式来做:

If you really need to mock it out, then I guess you'll have to do it the way you did:

$documentMock = { ... }

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

But that can break other things that rely on the $document service itself (such a directive that uses createElement, for instance).

更新

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

If you need to restore the document back to a consistent state after each test, you can do something along these lines:

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

Plunker 此处(我不得不使用另一个容器,否则不会呈现 Jasmine 结果).s>

Plunker here (I had to use another container otherwise Jasmine results wouldn't be rendered).

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

As @AlexanderNyrkov pointed out in the comments, both Jasmine and Karma have their own stuff inside the body tag, and wiping them out by emptying the document body doesn't seem like a good idea.

更新 2

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

I've managed to partially mock the $document service so you can use the actual page document and restore everything to a valid state:

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('');
});

Plunker 此处.

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

The idea is to replace the body tag with a new one that your SUT can freely manipulate and your test can safely clear at the end of every spec.

这篇关于Angular js 单元测试模拟文档的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆