【问题标题】:Testing meteor - testing allow / deny with a unit test instead of integration test测试流星 - 使用单元测试而不是集成测试来测试允许/拒绝
【发布时间】:2015-01-30 00:00:09
【问题描述】:

我有一个应用程序代码以下列方式限制文档

Docs.allow({
  insert: function(userId, doc){
    return !!userId
  },
  update: function(userId, doc){
    return userId && doc.owner == userId;
  }
})

目前,我只能运行进行实际 http 调用的集成测试。我无法在被测系统之外存根组件(Meteor 当前用户)(允许/拒绝规则)。

it("should succeed if user is authenticated", function(done) {
    Meteor.loginWithPassword(’shawn@abc.com', ‘hahaha', function(err){
        expect(err).toBe(undefined);
        Doc = Docs.insert({title: 'abc', 
                           category: 'Finance'}, 
                          function(err, id){
                              expect(err).toBeUndefined();
                              expect(id).not.toBeUndefined();
                              done();
                          });
    });
});

it("should fail if user is not authenticated", function(done) {
    Meteor.logout(function(){
        doc = Docs.insert({title: 'abc', 
                           category: 'Finance', 
                           owner: '1232131'}, 
                          function(err, id){
                              expect(err).not.toBeUndefined();
                              done();
                          });
    });
});

这使我的测试变得异常缓慢,尤其是当我要测试许多路径时。有没有办法让我将此测试移至较低级别的单元测试?

【问题讨论】:

    标签: unit-testing meteor jasmine integration-testing meteor-velocity


    【解决方案1】:

    基于 The Meteor Test Manual 的答案...Stories.allow mock 是在应用程序代码加载后定义的。因此,它没有效果。

    https://github.com/Sanjo/meteor-jasmine#stubs 中所述,

    tests/jasmine 文件夹(或其子文件夹)中以 -stubs.js 或 -stub.js 结尾的文件被视为存根并在应用代码之前加载。

    因此,要使 The Meteor Test Manual 的答案起作用,我们必须在 -stubs.js 文件中定义存根/模拟。这就是我在z-security-stubs.js 上所做的。

    请注意,我在文件名前加上了“z”,因为meteor 按字母顺序将文件加载到子目录的同一级别。我们必须确保在 Velocity 自动生成的package-stubs.jspackageMocksSpec.js 之后加载我们的自定义存根。

    考虑到这一点,z-security-stubs.js 可以包含如下内容:

    Mongo.Collection.prototype.allow = function(rules){
      this._velocityAllow = rules;
    }
    
    Mongo.Collection.prototype.deny = function(rules){
      this._velocityDeny = rules;
    }
    

    这会在集合实例的属性中保留对我们允许/拒绝安全规则的引用(例如,Docs、Files 或您的集合命名的任何内容);

    之后,我们可以参考该属性中的安全函数并进行断言:

    describe("Docs security rules", function() {
      var allow;
    
      beforeEach(function(){
        allow = Docs._velocityAllow;
      });
    
      it("insert deny access to non-logged in users", function() {
        var response = allow.insert(null, {});
        expect(response).toBe(false);
      });
    
      it("insert allow access to logged in users", function() {
        var response = allow.insert(true, {});
        expect(response).toBe(true);
      });
    
      it("update allow access to logged in users who are owners", function() {
        var response = allow.insert(2, {owner: 2});
        expect(response).toBe(true);
      });
    
      it("update deny access to non-logged in users", function() {
        var response = allow.update(null, {owner: 2});
        expect(response).toBe(false);
      });
    
      it("update deny access to logged in users who are not owners", function() {
        var response = allow.update(1, {owner: 2});
        expect(response).toBe(false);
      });
    });
    

    【讨论】:

    • 太好了!我在本地完成了完全相同的解决方案,并希望更改 Jasmine 不要强迫您使用 z-xxxx.js 作为存根。
    • 是的,但是如果我们有多个 .allow 插入/更新链,那么上面的操作就不会那么好,因为上面假设只有一个 _.velocityAllow.insert
    • 每个集合只能有一个插入/更新。所以在这里使用Docs._velocityAllow.insert 很好,当你有另一个收藏时,你使用AnotherCollection._velocityAllow.insert
    • docs.meteor.com/#/full/allow You can call allow as many times as you like, and each call can include any combination of insert, update, and remove functions. The functions should return true if they think the operation should be allowed. Otherwise they should return false, or nothing at all (undefined). In that case Meteor will continue searching through any other allow rules on the collection.
    【解决方案2】:

    此测试在服务器代码上练习执行路径,这是允许/拒绝规则所在的位置。

    这里的测试没有执行客户端和服务器之间的集成,这就是你的客户端集成测试做得很好。

    您可以使用单元测试覆盖代码中的所有执行路径,然后减少集成测试,从而获得您想要的速度。

    你应该尽可能多地进入较低的层次,但不是一切。您仍然希望确保集成也能正常工作。

    describe('Doc security rules', function () {
    
      var _oldAllowRules;
    
      beforeAll(function () {
        var _allowRules = null;
    
        // keep hold of the old Docs.allow method so we can restore in the after step
        _oldAllowRules = Docs.allow;
    
        // override the Docs.allow method so we can isolate the allow rules code
        Docs.allow = function (allowRules) {
          _allowRules = allowRules;
        };
      });
    
      afterAll(function () {
        // restore the Docs.allow method 
        Docs.allow = _oldAllowRules;
      });
    
      it('insert deny access to non-logged in users', function () {
        // Execute
        // you can now exercise the allowRules directly 
        var response = _allowRules.insert(null, {});
    
        // Verify
        expect(response).toBe(false);
      });
    
      it('update allows for doc owner', function () {
        // you can do it all with one step
        expect(_allowRules.update(1234, {owner: 1234})).toBe(true);
      });
    
      it('update denies for logged out user', function () {
        expect(_allowRules.update(null, {})).toBe(false);
      });
    
      it('update denies for non document owner', function () {
        expect(_allowRules.update(1234, {owner: 5678})).toBe(false);
      });
    
    });
    

    【讨论】:

    • 为什么这个测试包含在“服务器”测试中?我认为 .allow 规则仅在执行客户端插入时才被执行?服务器插入将完全绕过允许规则。 :(
    • 刚刚测试过了。 Cannot call method 'insert' of null。我认为Docs.allow 覆盖代码实际上从未被调用过。
    • 我认为 Jasmine 需要 beforeAll - 我刚刚更新了答案
    • 我以前用过。它对我不起作用。也许我遗漏了一些错字,但似乎我的应用程序代码是在测试代码之前加载的。所以这意味着那里的“.allow”模拟根本没有被调用
    • 我将尝试在本地工作并在此处更新。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-02
    • 1970-01-01
    • 1970-01-01
    • 2013-07-13
    • 2021-12-19
    • 2014-09-01
    相关资源
    最近更新 更多