【问题标题】:In Angularjs how do you alter a mock service per test?在 Angularjs 中,你如何改变每个测试的模拟服务?
【发布时间】:2018-04-08 11:32:37
【问题描述】:

代码示例

这可能在代码中得到更好的解释,所以我在下面包含了一个详细的抽象示例 - 我的问题在 cmets 中重复了。

示例摘要

例如,您模拟了一个相当复杂的模拟服务,并且在第一个测试中,您希望模拟服务对一个函数给出肯定的结果(在下面的示例中返回代码 200)。在第二个测试中,您希望相同的函数返回否定结果,例如 500。

我发现我可以简单地注入提供程序并覆盖方法,但这是正确的方法吗?请参阅下面的“测试 2”。

describe('CommentArea', function() {

  var controller
  beforeEach(function() {
    module('app', function($provide) {
      // Base mock definition
      $provide.provider('comments', function() {
        this.$get = function() {
          return {
            create: function() {
              return new Promise(function(resolve) {
                resolve({ code: 200 })
              })
            },
            delete: function() {
              return new Promise(function(resolve) {
                resolve({ code: 200 })
              })
            }
          }
        }
      })
    })

    inject(function($componentController) {
      controller = $componentController('CommentArea')
    })
  })

  // Start tests
  //
  // Test 1 - Use the above mocked service "as is" - happy path scenario
  //
  it('appends comment when successful', function() {
    expect(controller.commentCount).toEqual(0)
    controller.create() // uses the comments service create method
    expect(controller.commentCount).toEqual(1)    
  })

  // Test 2 - Use the above mocked service with a slight modification
  // This time the create function will fail
  //
  it('does not append comment when unsuccessful', inject(function(comments) {
    // overwrite for failure condition here
    // QUESTION - is this acceptable? It seems odd just overwriting it
    // so directly
    comments.create = function () {
      return new Promise(function(resolve) {
        resolve({ code: 500 })
      })
    }

    expect(controller.commentCount).toEqual(0)
    controller.create() // uses the comments service create method
    expect(controller.commentCount).toEqual(0)    
  }))
})

【问题讨论】:

    标签: javascript angularjs unit-testing angular-services


    【解决方案1】:

    通常这意味着不应在beforeEach 中定义模拟返回值。

    其中一种方法是让函数返回在通用函数范围(describe 块)中定义的局部变量:

    var createValue;
    ...
    beforeEach(module('app', { comments: {
      create: function () { return createValue }, ...
    }))
    ...
    createValue = Promise.resolve(200);
    comments.create();
    

    局部变量有一个缺点。如果createValue 在下次测试中没有被错误地重新定义,测试可能会被交叉污染。

    此外,这种模拟并不能解决重要的任务;模拟函数不会被监视。

    这正是茉莉间谍应该解决的任务:

    beforeEach(function () {
      module('app', { comments: jasmine.createSpyObj('comments', ['create', 'delete']) });
    });
    

    module 在这种情况下应该用一个函数包装,因为应该在每个beforeEach 调用上创建一个新的间谍对象。

    然后就地定义返回值:

    comment.create.and.returnValue(Promise.resolve(200))
    comments.create();
    

    【讨论】:

    • 这太棒了 - 刚刚试了一下,效果很好。我没有完全理解它——所以我需要离开去复习几次,让它在我的大脑中安定下来。谢谢!
    • 对不起 - 你能澄清一下“应该用函数包装”吗?
    • 请注意,在第一个示例中它是beforeEach(module(..)),在第二个示例中它是beforeEach(function () { module(...) })。两者都适用于module,但后者允许在每个规范上更新jasmine.createSpyObj(...),这是理想的行为。
    猜你喜欢
    • 2014-05-08
    • 1970-01-01
    • 2013-04-07
    • 2015-04-01
    • 1970-01-01
    • 2011-01-28
    • 2015-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多