开发插件

Karma可以通过插件进行扩展。插件有五种类型:框架报告器启动器预处理器中间件。每种类型都允许修改Karma行为的特定方面。

  • 框架将测试框架(如Mocha)连接到Karma API,以便浏览器可以将测试结果发送回Karma服务器。
  • 报告器定义如何向用户报告测试结果。
  • 启动器允许Karma启动不同的浏览器来运行测试。
  • 预处理器负责在将源文件加载到浏览器之前对其进行转换/转译。
  • 中间件可用于自定义如何将文件提供给浏览器。

依赖注入 #

Karma是使用依赖注入构建的。理解这个概念对于开发插件非常重要。

在非常高的层面上,您可以将Karma视为一个对象,其中每个键(一个DI令牌)都映射到某个Karma对象(一个服务)。例如,config DI令牌映射到Config实例,该实例保存当前的Karma配置。插件可以通过指定相应的DI令牌来请求(或注入)各种Karma对象。注入后,插件可以与注入的服务交互以实现其功能。

没有所有可用服务及其DI令牌的详尽列表,但您可以通过阅读Karma或其他插件的源代码来发现它们。

插件结构 #

每个插件本质上都是一个与其关联的DI令牌的服务。当用户在他们的配置中激活插件时,Karma会查找相应的DI令牌并实例化链接到此DI令牌的服务。

要声明插件,应该为插件定义一个DI令牌并向Karma解释如何实例化它。DI令牌由两部分组成:插件类型和插件的唯一名称。前者定义了插件可以做什么、对服务API的要求以及何时实例化它。后者是一个唯一的名称,插件用户将使用它来激活插件。

插件定义多个服务是完全有效的。这可以通过向插件导出的对象添加更多键来完成。常见的例子是framework + reporter插件,它们通常一起出现。

让我们创建一个非常简单的插件,在实例化时打印“Hello, world!”。我们将使用framework类型,因为它在Karma生命周期的早期被实例化并且对它的API没有任何要求。让我们将我们的插件命名为“hello”,因此它的唯一名称将是hello。将这两部分组合起来,我们得到插件的DI令牌framework:hello。让我们声明它。

// hello-plugin.js

// A factory function for our plugin, it will be called, when Karma needs to
// instantiate a plugin. Normally it should return an instance of the service
// conforming to the API requirements of the plugin type (more on that below),
// but for our simple example we don't need any service and just print 
// a message when function is called.
function helloFrameworkFactory() {
  console.log('Hello, world!')
}

module.exports = {
  // Declare the plugin, so Karma knows that it exists.
  // 'factory' tells Karma that it should call `helloFrameworkFactory`
  // function and use whatever it returns as a service for the DI token
  // `framework:hello`.
  'framework:hello': ['factory', helloFrameworkFactory]
};
// karma.conf.js

module.exports = (config) => {
  config.set({
    plugins: [
      require('./hello-plugin')
    ],
    // Activate our plugin by specifying its unique name in the 
    // corresponding configuration key.
    frameworks: ['hello']
  })
}

注入依赖项 #

在“依赖注入”部分中,我们讨论了可以将任何Karma服务注入插件并与之交互。这可以通过将$inject属性设置为插件希望与之交互的DI令牌数组来完成。Karma将获取此属性并将请求的服务作为参数传递给工厂函数。

让我们使hello框架更有用一些,并使其将hello.js文件添加到files数组中。这样,插件的用户可以例如从他们的测试中访问hello.js中定义的函数。

// hello-plugin.js

// Add parameters to the function to receive requested services.
function helloFrameworkFactory(config) {
  config.files.unshift({
    pattern: __dirname + '/hello.js',
    included: true,
    served: true,
    watched: false
  })
}

// Declare DI tokens plugin wants to inject.
helloFrameworkFactory.$inject = ['config']

module.exports = {
  'framework:hello': ['factory', helloFrameworkFactory]
};

Karma配置保持不变,为了简洁起见省略了。请参阅上面的示例以了解插件的使用。

注意

目前,Karma使用node-di库作为DI实现。该库比上面记录的更强大,但是,DI实现将来可能会发生变化,因此我们建议不要依赖于node-di的实现细节。

插件类型 #

本节概述了不同插件类型的API要求和约定。还有一些插件链接,您可以使用它们作为灵感。

框架 #

框架将现有的测试库连接到Karma的API,以便它们的成果可以在浏览器中显示并发送回服务器。

Karma框架必须实现window.__karma__.start方法,Karma将调用该方法来启动测试执行。此函数使用一个对象调用,该对象具有将结果发送回karma的方法

  • .result 单个测试已完成
  • .complete 客户端已完成所有测试的执行
  • .error 客户端发生错误
  • .info 其他数据(例如测试数量或调试消息)

最常见的是您将使用result方法发送单个测试成功或失败状态。该方法采用以下形式的对象

{
    // test id
    id: String,

     // test description
    description: String,

    // the suite to which this test belongs. potentially nested.
    suite: Array[String],

    // an array of string error messages that might explain a failure.
    // this is required if success is false.
    log: Array[String],

    success: Boolean, // pass / fail

    skipped: Boolean // skipped / ran
}

报告器 #

启动器 #

预处理器 #

预处理器是一个函数,它接受三个参数(contentfilenext),以某种方式修改内容,并将其传递给下一个预处理器。

  • 传递给预处理器插件的参数
    • content 正在处理的文件的内容
    • file 描述正在处理的文件的对象
      • path: 当前文件,可变文件路径。例如some/file.coffee -> some/file.coffee.js 此路径是可变的,可能实际上不存在。
      • originalPath: 原始的、未更改的路径
      • encodings: 一个可变的键控对象,其中键是有效的编码类型('gzip'、'compress'、'br'等),值是编码后的内容。编码后的内容应存储在此处,而不是使用next(null, encodedContent)解析。
      • type: 确定在提供服务时如何包含文件。
    • next 预处理完成后要调用的函数,应调用为next(null, processedContent)next(error)

更复杂的东西 #

由于Karma是由依赖注入构建的,因此插件可以请求几乎任何Karma组件并与之交互。有一些插件可以做更多有趣的事情,例如,查看karma-closurekarma-intellij