Wie teste ich Chrome-Erweiterungen?

Lesezeit: 8 Minuten

Benutzer-Avatar
sumpfjohn

Gibt es eine gute Möglichkeit, dies zu tun? Ich schreibe eine Erweiterung, die als Inhaltsskript mit einer Website interagiert und Daten mithilfe von Localstorage speichert. Gibt es Tools, Frameworks usw., mit denen ich dieses Verhalten testen kann? Mir ist klar, dass es einige generische Tools zum Testen von Javascript gibt, aber sind diese ausreichend leistungsfähig, um eine Erweiterung zu testen? Unit-Tests sind am wichtigsten, aber ich interessiere mich auch für andere Arten von Tests (z. B. Integrationstests).

  • Ich habe gerade eine kanonische Antwort geschrieben, die sich mit Komponententests und Integrationstests für Browsererweiterungen befasst alle Browser, nicht nur Chrome. Siehe die Antwort auf „Testen von Browsererweiterungen“.

    – Rob W

    28. Juni 2013 um 21:52 Uhr

Benutzer-Avatar
Kinlan

Ja, die vorhandenen Frameworks sind ziemlich nützlich.

In der jüngeren Vergangenheit habe ich alle meine Tests auf einer “Test”-Seite platziert, die in die Anwendung eingebettet war, aber nicht erreichbar war, es sei denn, sie wurde physisch eingegeben.

Zum Beispiel hätte ich alle Tests auf einer Seite, auf die unter zugegriffen werden kann chrome-extension://asdasdasdasdad/unittests.html

Die Tests müssten Zugriff haben localStorage usw. Für den Zugriff auf Inhaltsskripte könnten Sie dies theoretisch durch eingebettete IFRAMEs in Ihrer Testseite testen. Dies sind jedoch eher Tests auf Integrationsebene. Komponententests erfordern, dass Sie dies von echten Seiten abstrahieren, damit Sie nicht von ihnen abhängig sind , ebenfalls mit Zugriff auf localStorage.

Wenn Sie Seiten direkt testen möchten, können Sie Ihre Erweiterung orchestrieren, um neue Tabs zu öffnen (chrome.tab.create({“url” : “someurl”}). Für jeden der neuen Tabs sollte Ihr Inhaltsskript ausgeführt werden und Sie können ihn verwenden Ihr Testframework, um zu überprüfen, ob Ihr Code das getan hat, was er tun sollte.

Was Rahmen angeht, JsUnit oder die neueren Jasmin sollte gut funktionieren.

  • Sie haben Recht, das Testen echter Seiten fällt nicht unter Unit-Tests. Ich hätte meine Frage breiter stellen sollen. Aber es ist immer noch etwas, das ich gerne testen würde, zumal sich die HTML-Struktur der Website jederzeit ändern kann. Ich habe die Frage geändert.

    – sumpfjohn

    24. Mai 2010 um 19:39 Uhr


  • Ich würde immer noch durch IFrames auf Ihrer Komponententestseite testen. Die Inhaltsskripts sollten weiterhin ausgelöst werden (wenn Sie die Ausführung der Skripts im iFrame aktivieren).

    – Kinlan

    24. Mai 2010 um 19:55 Uhr

  • Die Proxy-Beispielerweiterung enthält einige Tests, die nur die notwendigen Teile der Chrome-APIs nachahmen: code.google.com/chrome/extensions/samples.html#chrome.proxy .. Auch unser Kollege Boris hat QUnit zum Testen seiner “Modell”-Schicht verwendet: github.com/borismus/Question-Monitor-for-Stack-Exchange/tree/…

    – Paul Irisch

    17. Januar 2012 um 20:27 Uhr


Benutzer-Avatar
lebensnotwendig

Ich habe an mehreren Chrome-Erweiterungen gearbeitet, die ich mir ausgedacht habe sinon-chrome Projekt, mit dem Unit-Tests ausgeführt werden können mocha, nodejs und phantomjs.

Im Grunde erstellt es Sinon-Mocks von allen chrome.* API, in die Sie beliebige vordefinierte JSON-Antworten einfügen können.

Als Nächstes laden Sie Ihre Skripte mithilfe von Knoten vm.runInNewContext für Hintergrundseite u phantomjs für Render-Popup / Optionsseite.

Und schließlich behaupten Sie, dass die Chrome-API mit den erforderlichen Argumenten aufgerufen wurde.

Nehmen wir ein Beispiel:

Angenommen, wir haben eine einfache Chrome-Erweiterung, die die Anzahl der geöffneten Registerkarten im Schaltflächenabzeichen anzeigt.

Hintergrundseite:

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

Zum Testen brauchen wir:

  1. spotten chrome.tabs.query um eine vordefinierte Antwort zurückzugeben, z. B. zwei Tabulatoren.
  2. spritzen unsere verspotteten chrome.* api in eine Umgebung
  3. Führen Sie unseren Erweiterungscode in dieser Umgebung aus
  4. behaupten, dass das Button-Badge gleich „2“ ist

Das Code-Snippet lautet wie folgt:

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"
});

Jetzt können wir es in Mokkas einpacken describe..it Funktionen und vom Terminal ausführen:

$ mocha

background page
  ✓ should display opened tabs count in button badge

1 passing (98ms)

Sie können ein vollständiges Beispiel finden hier.

Darüber hinaus ermöglicht Sinon-Chrome das Auslösen eines beliebigen Chrome-Ereignisses mit vordefinierter Reaktion, z

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

  • Der Link für das Beispiel scheint tot zu sein – könnten Sie ihn bitte aktualisieren?

    – Auferstanden

    7. Dezember 2015 um 20:55 Uhr

  • Link zum Beispiel aktualisiert. Auch Sinon-Chrome ist jetzt umgezogen github.com/acvetkov und es wird bald neue Beispiele geben

    – vitalets

    10. Dezember 2015 um 14:13 Uhr

  • Hinweis: Das Projekt scheint veraltet zu sein: Der letzte Commit war aktiviert 26. November 2019Fragen bleiben unbeantwortet.

    – miwe

    8. Mai 2021 um 18:27 Uhr

Während sinon.js scheint großartig zu funktionieren, Sie können auch einfach Jasmine verwenden und die Chrome-Callbacks verspotten, die Sie benötigen. Beispiel:

Spotten

chrome = {
  runtime: {
    onMessage : {
      addListener : function() {}
    }
  }
}

Prüfen

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

Ändern Sie einfach die Standardeinstellung SpecRunner.html um Ihren Code auszuführen.

Benutzer-Avatar
Nafis Ahmad

Über bereits vorhandenes Tool in Chrome:

  1. Im Chrome-Entwicklertool gibt es einen Abschnitt für Ressourcen für die lokale Speicherung.

    Entwicklertools > Ressourcen > Lokaler Speicher

    Sehen Sie dort die Änderungen von localstorage.

  2. Sie können console.profile verwenden, um die Leistung zu testen und die Aufrufliste zur Laufzeit zu überwachen.

  3. für fileSystem Sie können diese URL verwenden, um zu überprüfen, ob Ihre Datei hochgeladen wurde oder nicht: filesystem:chrome-extension:///temporary/

Wenn Sie das Inhaltsskript und den lokalen Speicher zusammen ohne Hintergrundseite/Skript und ohne Nachrichtenübermittlung verwenden, ist der lokale Speicher nur von dieser Site aus zugänglich. Um diese Seiten zu testen, müssen Sie also Ihr Testskript in diese Registerkarten einfügen.

Ich fand, dass ich verwenden kann Selenium-Webtreiber zum Starten einer frischen Browserinstanz mit vorinstallierter Erweiterung und pyautogui für Klicks – da Selenium die Erweiterung nicht “ansehen” kann. Nach Klicks können Sie Screenshots machen und sie mit “erwarteten” vergleichen, wobei Sie eine Ähnlichkeit von 95 % erwarten (weil es bei verschiedenen Browsern akzeptable Markup-Bewegungen von wenigen Pixeln sind).

Benutzer-Avatar
Jon A

Um ein paar vorherige Antworten zu bestätigen, scheint Jasmine gut mit Chrome-Erweiterungen zu funktionieren. Ich verwende Version 3.4.0.

Sie können verwenden Jasmin-Spione um einfach Testdoubles für die verschiedenen APIs zu erstellen. Keine Notwendigkeit, Ihre eigene von Grund auf neu zu bauen. Zum Beispiel:

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);

  });

});

Noch ein paar Details, falls es hilft:

Wie in einer anderen Antwort erwähnt, habe ich eine HTML-Seite als Teil meiner Browsererweiterung erstellt, die meine Tests ausführt. Die HTML-Seite enthält die Jasmine-Bibliothek, den JavaScript-Code meiner Erweiterung und meine Testsuite. Die Tests werden automatisch ausgeführt und die Ergebnisse werden für Sie formatiert. Es ist nicht erforderlich, einen Test-Runner oder einen Ergebnisformatierer zu erstellen. Folgen Sie einfach der Installationsanleitungund verwenden Sie das dort dokumentierte HTML, um Ihre Test Runner-Seite zu erstellen, und fügen Sie auch Ihre Testsuite in die Seite ein.

Ich glaube nicht, dass Sie das Jasmine-Framework dynamisch von einem anderen Host abrufen können, also habe ich einfach die Jasmine-Version in meine Erweiterung aufgenommen. Ich werde es natürlich weglassen und auch meine Testfälle, wenn ich meine Erweiterung für die Produktion baue.

Ich habe mir nicht angesehen, wie ich meine Tests auf der Befehlszeile ausführe. Das wäre praktisch für automatisierte Bereitstellungstools.

Benutzer-Avatar
fxnoob

Zum Testen von End-to-End können Sie verwenden puppeteer. Hier ist ein Ausschnitt, den ich für meine Erweiterung geschrieben habe, um die geladene Erweiterung zu überprüfen title und um zu überprüfen, ob die Erweiterung im Inkognito-Modus aktiviert wurde.

const path = require("path");
const puppeteer = require("puppeteer");
const assert = require("assert");
const Constants = require("../contants");
const Utils = require("./util");

const extensionID = Constants.EXTENSION_ID;
const extensionPath = path.join(__dirname, "../dist");
const extensionOptionHtml = "option.html";
const extPage = `chrome-extension://${extensionID}/${extensionOptionHtml}`;
let extensionPage = null;
let browser = null;

async function boot() {
  browser = await puppeteer.launch({
    // slowMo: 250,
    headless: false, // extension are allowed only in head-full mode
    args: [
      `--disable-extensions-except=${extensionPath}`,
      `--load-extension=${extensionPath}`,
      "--no-sandbox",
      "--disable-setuid-sandbox"
    ]
  });

  extensionPage = await browser.newPage();
  await extensionPage.goto(extPage);
}

describe("Extension UI Testing", function() {
  this.timeout(20000); // default is 2 seconds and that may not be enough to boot browsers and pages.
  before(async function() {
    await boot();
  });

  describe("option page home", async function() {
    it("check title", async function() {
      const h1 = "Allow extension in Incognito Mode";
      const extH1 = await extensionPage.evaluate(() =>
        document.querySelector("h1").textContent.trim()
      );
      assert.equal(extH1, h1);
    });
    it("show option ui after enabling extension in incognito", async () => {
      await extensionPage.goto(`chrome://extensions/?id=${extensionID}`);
      extensionPage.evaluate(() =>
        document
          .querySelector("body > extensions-manager")
          .shadowRoot.querySelector("#viewManager > extensions-detail-view")
          .shadowRoot.querySelector("#allow-incognito")
          .shadowRoot.querySelector("#crToggle")
          .click()
      );
      await Utils.sleep(2000);
      await extensionPage.goto(
        `chrome-extension://${extensionID}/${extensionOptionHtml}`
      );
      const h3 = "Mark Incognito";
      const headingID = `#${Constants.OPTION_SCRIPT_HOST_ID} > div > div > header > div > h6`;
      await extensionPage.waitFor(headingID);
      console.log({ headingID });
      const extH3 = await extensionPage.evaluate(headingID => {
        return document.querySelector(headingID).textContent.trim();
      }, headingID);
      console.log({ extH3 });
      assert.equal(extH3, h3);
    });
  });

  after(async function() {
    await browser.close();
  });
});

1286930cookie-checkWie teste ich Chrome-Erweiterungen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy