关于单元测试:如何测试chrome扩展?

How to test chrome extensions?

有没有办法做到这一点? 我正在编写一个与网站交互作为内容脚本的扩展程序,并使用localstorage保存数据。 是否有可用于测试此行为的工具,框架等? 我意识到有一些用于测试javascript的通用工具,但是那些足以测试扩展的能力吗? 单元测试是最重要的,但我也对其他类型的测试(例如集成测试)感兴趣。


是的,现有的框架非常有用..

在最近的过去,我已将所有测试放在嵌入到应用程序中的"测试"页面中,但除非是物理类型,否则无法访问。

例如,我可以在chrome-extension://asdasdasdasdad/unittests.html下访问页面中的所有测试

测试可以访问localStorage等。对于访问内容脚本,理论上你可以通过测试页面中的嵌入式IFRAME测试,但是这些是更多的集成级测试,单元测试需要你从实际页面中抽象出来所以你不依赖它们,同样可以访问localStorage。

如果您想直接测试页面,可以编排扩展程序以打开新选项卡(chrome.tab.create({"url":"someurl"})。对于每个新选项卡,您的内容脚本应该运行并且您可以使用您的测试框架,以检查您的代码是否已完成应该执行的操作。

至于框架,JsUnit或更新的Jasmine应该可以正常工作。


在几个chrome扩展上工作时,我提出了sinon-chrome项目,该项目允许使用mochanodejsphantomjs运行单元测试。

Basicaly,它创建了所有chrome.* API的sinon模拟,您可以在其中放置任何预定义的json响应。

接下来,使用节点的vm.runInNewContext为后台页面加载脚本,为渲染弹出/选项页面加载phantomjs

最后,你断言chrome api是用所需的参数调用的。

我们来举个例子:
假设我们有简单的chrome扩展,显示按钮徽章中打开的标签数量。

背景页面:

1
2
3
chrome.tabs.query({}, function(tabs) {
  chrome.browserAction.setBadgeText({text: String(tabs.length)});
});

为了测试它,我们需要:

  • mock chrome.tabs.query返回预定义的响应,例如两个标签。
  • 将我们的模拟chrome.* api注入某个环境
  • 在此环境中运行我们的扩展代码
  • 断言按钮徽章等于'2'
  • 代码段如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    const vm = require('vm');
    const fs = require('fs');
    const chrome = require('sinon-chrome');

    // 1. mock `chrome.tabs.query` to return predefined response
    chrome.tabs.query.yields([
      {id: 1, title: 'Tab 1'},
      {id: 2, title: 'Tab 2'}
    ]);

    // 2. inject our mocked chrome.* api into some environment
    const context = {
      chrome: chrome
    };

    // 3. run our extension code in this environment
    const code = fs.readFileSync('src/background.js');
    vm.runInNewContext(code, context);

    // 4. assert that button badge equals to '2'
    sinon.assert.calledOnce(chrome.browserAction.setBadgeText);
    sinon.assert.calledWithMatch(chrome.browserAction.setBadgeText, {
      text:"2"
    });

    现在我们可以将它包装到mocha的describe..it函数中并从终端运行:

    1
    2
    3
    4
    5
    6
    $ mocha

    background page
      ? should display opened tabs count in button badge

    1 passing (98ms)

    你可以在这里找到完整的例子。

    此外,sinon-chrome允许触发任何具有预定义响应的chrome事件,例如

    1
    chrome.tab.onCreated.trigger({url: 'http://google.com'});


    关于Chrome中已有的工具:

  • 在chrome开发人员工具中,有适用于本地存储的资源部分。

    开发者工具>资源>本地存储

    查看当地存储的变化。

  • 您可以使用console.profile来测试性能并观察运行时调用堆栈。

  • for fileSystem您可以使用此URL检查您的文件是否已上传:
    文件系统:铬扩展:///临时/
  • 如果您在没有后台页面/脚本且没有消息传递的情况下一起使用内容脚本和本地存储,则只能从该站点访问本地存储。
    因此,要测试这些页面,您必须在这些选项卡中注入测试脚本。


    虽然sinon.js看起来效果很好,但您也可以使用简单的Jasmine并模拟您需要的Chrome回调。例:

    嘲笑

    1
    2
    3
    4
    5
    6
    7
    chrome = {
      runtime: {
        onMessage : {
          addListener : function() {}
        }
      }
    }

    测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    describe("JSGuardian", function() {

      describe("BlockCache", function() {

        beforeEach(function() {
          this.blockCache = new BlockCache();
        });

        it("should recognize added urls", function() {
          this.blockCache.add("http://some.url");
          expect(this.blockCache.allow("http://some.url")).toBe(false);
        });
    } // ... etc

    只需修改默认SpecRunner.html即可运行代码。


    为了确认以前的几个答案,Jasmine似乎与Chrome扩展程序配合得很好。我使用的是3.4.0版本。

    您可以使用Jasmine间谍轻松地为各种API创建测试双精度。无需从头开始构建自己的。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    describe("Test suite", function() {

      it("Test case", function() {

        // Set up spies and fake data.
        spyOn(chrome.browserAction,"setPopup");
        spyOn(chrome.identity,"removeCachedAuthToken");
        fakeToken ="faketoken-faketoken-faketoken";
        fakeWindow = jasmine.createSpyObj("window", ["close"]);

        // Call the function under test.
        logout(fakeWindow, fakeToken);

        // Perform assertions.
        expect(chrome.browserAction.setPopup).toHaveBeenCalledWith({popup:""});
        expect(chrome.identity.removeCachedAuthToken).toHaveBeenCalledWith({token: fakeToken});
        expect(fakeWindow.close.calls.count()).toEqual(1);

      });

    });

    更多细节,如果它有帮助:

    正如另一个答案中所提到的,我创建了一个HTML页面作为浏览器扩展的一部分来运行我的测试。 HTML页面包括Jasmine库,以及我的扩展程序的JavaScript代码,以及我的测试套件。测试会自动运行,并为您格式化结果。无需构建测试运行器或结果格式化程序。只需按照安装说明操作,并使用那里记录的HTML来创建测试运行器页面,并在页面中包含您的测试套件。

    我不认为你可以从其他主机动态获取Jasmine框架,所以我只是在我的扩展中包含了Jasmine版本。当然,当我为生产构建扩展时,我将省略它以及我的测试用例。

    我没有看过如何在命令行执行我的测试。这对于自动部署工具来说非常方便。


    我发现我可以使用Selenium web驱动程序启动带有预安装扩展的新浏览器实例和用于点击的pyautogui - 因为Selenium无法驱动扩展的"视图"。点击后,您可以制作屏幕截图并将其与"预期"的屏幕截图进行比较,期望95%的相似度(因为在不同的浏览器上,标记运动可以接受几个像素)。