6

我想提供简单的常量值,例如姓名、电子邮件等,以在我的 jasmine 单元测试中使用。

这里问了一个类似的问题:AngularJS + Karma:在单元测试指令或控制器时重用模拟服务

在 c# 中,我将创建一个静态类来保存模拟数据的小片段。然后我可以在整个单元测试中使用这些值,如下所示:

static class SampleData
{
    public const string Guid = "0e3ae555-9fc7-4b89-9ea4-a8b63097c50a";
    public const string Password = "3Qr}b7_N$yZ6";

    public const string InvalidGuid = "[invalid-guid]";
    public const string InvalidPassword = "[invalid-password]"; 
}

在使用 Karma / Jasmine 测试我的 AngularJS 应用程序时,我希望获得同样的便利。

我知道我可以constant针对我的 Angular 应用程序定义一个对象,我已经为我在实际代码中使用的常量执行了此操作,如下所示:

myApp.constant('config', {apiUrl:'http://localhost:8082'})

我可以像这样添加另一个常量,但只包含用于单元测试的示例数据值,如下所示:

myApp.constant('sampleData', {email: 'sample@email.com'}) 

然后我可以将模拟常量对象注入到我的测试中,然后我就走了,就像这样

describe 'A sample unit test', ->
    beforeEach ->   module 'myApp'
    beforeEach inject ($injector) ->
        @sampleData = $injector.get 'sampleData'
        email = @sampleData.email
        # etc ...

然而,这对我来说似乎有点可疑。我不希望我的生产代码包含仅由我的单元测试需要的示例数据。

您将如何方便地为您的 angular / jasmine 单元测试提供可重用的样本数据值?

谢谢

4

1 回答 1

10

有两种方法可以做到这一点:

  • 监视函数调用并返回假值。
  • 创建模拟类(可能还有模拟数据来初始化它们)并在需要的地方加载它们

当您只需要伪造几个电话时,第一个就可以了。为整个班级这样做是不可持续的。

例如,假设您有一个构建一些特殊 URL 的服务。如果其中一种方法依赖于 absUrl,您可以通过监视$location对象中的方法来伪造它:

describe('example') function () {
    beforeEach(inject(function () {
        spyOn($location, 'absUrl').andCallFake(function (p) {
            return 'http://localhost/app/index.html#/chickenurl';
        });
    }));
it('should return the url http://www.chicken.org') ... {
    // starting situation
    // run the function
    // asserts
}

现在假设您有一个Settings服务,它封装了语言、主题、特殊路径、背景颜色、字体等数据,这些数据是使用远程调用服务器进行初始化的。测试依赖的服务Settings会很痛苦。你必须每次都用 spyOn 模拟一个大组件。如果您有 10 个服务...您不想在所有服务中复制粘贴 spyOn 功能。

ServiceA使用Settings服务:

describe('ServiceA', function () {
var settings, serviceA;

beforeEach(module('myapp.mocks.settings'));  // mock settings
beforeEach(module('myapp.services.serviceA')); // load the service being tested

beforeEach(inject(function (_serviceA_, _settings_) {
    serviceA = _serviceA_;
    settings = _settings_;
}));

这个测试套件的容器,对Settings服务的所有调用都将由模拟处理,模拟与真实的具有相同的接口,但返回虚拟值。请注意,您可以在任何地方加载此服务。

(如果出于任何原因,您需要使用真实的实现,您可以在模拟之前加载真实的实现,并针对该特定情况使用 spyOn 将调用委托给真实的实现。)

通常你会将 mocks 模块放在 app 文件夹之外。我有一个test包含单元测试、e2e 测试的lib文件夹和一个包含 angular-mocks.js 文件的文件夹。我也把我的模拟放在那里。告诉 karma 测试所需的文件:

    files: [
      'app/lib/jquery/jquery-1.9.1.js',
      'test/lib/jasmine-jquery.js',
      'app/lib/angular/angular.js',
      'app/lib/angular/angular-*.js',
      'test/lib/angular/angular-mocks.js',
      'test/lib/myapp/*.js', /* this is mine */
      'app/js/**/*.js',
      'test/unit/**/*.js'
    ],

该文件tests/lib/myapp.mocks.settings.js看起来就像任何其他模块:

(function () {
"use strict";

var m = angular.module('myapp.mocks.settings', []);

m.service('settings', function () { ... })
})

第二个问题(可选):您想快速更改虚拟值。在示例中,设置服务在第一次实例化时从服务器获取对象。然后,该服务具有所有领域的吸气剂。这是一种对服务器的代理:不是每次需要值时都发送请求,而是获取一堆并将它们保存在本地。在我的应用程序中,运行时服务器中的设置不会更改。

就像是:

1. fetch http://example.org/api/settings
2. var localSettings = fetchedSettings;
3  getFieldA: function() { return localSettings.fieldA; }

去污染全局命名空间。我创建了一个文件settings.data.js,其内容如下:

var SETTINGS_RESPONSE = { fieldA: 42 };

模拟服务使用工厂中的这个全局变量来实例化localSettings

于 2013-10-30T12:39:25.133 回答