次の方法で共有


Office アドインでの単体テスト

単体テストでは、Office アプリケーションへの接続など、ネットワーク接続やサービス接続を必要とせずに、アドインの機能をチェックします。 サーバー側の単体テスト コードと、Office JavaScript API を呼び出さないクライアント側コードは、Office アドインの場合とどの Web アプリケーションでも同じであるため、特別なドキュメントは必要ありません。 ただし、Office JavaScript API を呼び出すクライアント側のコードは、テストが困難です。 これらの問題を解決するために、単体テストでのモック Office オブジェクトの作成を簡略化するライブラリを作成しました: Office-Addin-Mock。 ライブラリを使用すると、次の方法でテストが容易になります。

  • Office JavaScript API は、Office アプリケーション (Excel、PowerPoint、Word など) のコンテキストで Webview コントロールで初期化する必要があります。そのため、開発用コンピューターで単体テストを実行するプロセスで読み込む必要があります。 Office-Addin-Mock ライブラリをテスト ファイルにインポートできます。これにより、テストを実行する Node.js プロセス内で Office JavaScript API をモックできます。
  • アプリケーション固有の API には、他の関数と相互に対して特定の順序で呼び出す必要がある読み込みおよび同期メソッドがあります。 さらに、テスト中の関数の後半でコードによって読み取られる Office オブジェクトのプロパティに応じて、特定のパラメーターで load メソッドを呼び出す必要があります。 ただし、単体テスト フレームワークは本質的にステートレスであるため、 load または sync が呼び出されたかどうか、または loadに渡されたパラメーターの記録を保持することはできません。 Office-Addin-Mock ライブラリを使用して作成するモック オブジェクトには、これらのことを追跡する内部状態があります。 この内部状態により、モック オブジェクトは実際の Office オブジェクトのエラー動作をエミュレートできます。 たとえば、テスト対象の関数が、最初に load に渡されなかったプロパティを読み取ろうとした場合、テストは Office から返されるエラーと同様のエラーを返します。

ライブラリは Office JavaScript API に依存せず、次のような任意の JavaScript 単体テスト フレームワークで使用できます。

この記事の例では、Jest フレームワークを使用します。 Mocha フレームワークを使用する例については、 Office-Addin-Mock ホーム ページを参照してください。

前提条件

この記事では、テスト ファイルを作成して実行する方法など、単体テストとモックの基本的な概念に精通していること、および単体テスト フレームワークに関する経験があることを前提としています。

ヒント

Microsoft Visual Studio (VS) を使用している場合は、VS での JavaScript 単体テストに関する基本的な情報については、 Visual Studio での JavaScript と TypeScript の単体テスト に関する記事をお読みになり、この記事に戻ってください。

ツールをインストールする

ライブラリをインストールするには、コマンド プロンプトを開き、アドイン プロジェクトのルートに移動し、次のコマンドを入力します。

npm install office-addin-mock --save-dev

基本的な使用法

  1. プロジェクトには、1 つ以上のテスト ファイルがあります。 (以下の例では、テスト フレームワークの手順とテスト ファイルの例を参照してください)。次の例に示すように、requireまたはimport キーワード (keyword)を使用して、Office JavaScript API を呼び出す関数のテストがあるテスト ファイルにライブラリをインポートします。

    // CommonJS
    const OfficeAddinMock = require("office-addin-mock");
    
    // ES6
    import OfficeAddinMock from "office-addin-mock";
    
  2. requireまたはimport キーワード (keyword)でテストするアドイン関数を含むモジュールをインポートします。 次の例では、テスト ファイルがアドインのコード ファイルを含むフォルダーのサブフォルダー内にあると仮定します。

    // CommonJS
    const myOfficeAddinFeature = require("../my-office-add-in");
    
    // ES6
    import myOfficeAddinFeature from "../my-office-add-in";
    
  3. 関数をテストするためにモックする必要があるプロパティとサブプロパティを持つデータ オブジェクトを作成します。 次の例は、Excel Workbook.range.address プロパティと Workbook.getSelectedRange メソッドをモックするオブジェクトを示しています。 このオブジェクトは最終的なモック オブジェクトではありません。 これは、最終的なモック オブジェクトを作成するために使用 OfficeMockObject シード オブジェクトと考えてください。

    const mockData = {
      workbook: {
        range: {
          address: "C2:G3",
        },
        getSelectedRange: function () {
          return this.range;
        },
      },
    };
    
  4. OfficeMockObject コンストラクターにデータ オブジェクトを渡します。 返される OfficeMockObject オブジェクトについては、次の点に注意してください。

    • これは、OfficeExtension.ClientRequestContext オブジェクトの簡略化されたモックです。
    • モック オブジェクトには、データ オブジェクトのすべてのメンバーが含まれており、 load メソッドと sync メソッドのモック実装もあります。
    • モック オブジェクトは、 ClientRequestContext オブジェクトの重要なエラー動作を模倣します。 たとえば、テスト対象の Office API がプロパティを読み込んで syncを呼び出さずにプロパティを読み取ろうとした場合、テストは運用ランタイムでスローされるエラー "Error, property not loaded" と同様のエラーで失敗します。
    const contextMock = new OfficeAddinMock.OfficeMockObject(mockData);
    

    注:

    OfficeMockObject型の完全なリファレンス ドキュメントは、Office-Addin-Mock にあります

  5. テスト フレームワークの構文で、 関数のテストを追加します。 モックするオブジェクトの代わりに、 OfficeMockObject オブジェクト (この場合は ClientRequestContext オブジェクト) を使用します。 Jest の例を次に示します。 このテスト例では、テスト対象のアドイン関数が getSelectedRangeAddress呼び出され、 ClientRequestContext オブジェクトがパラメーターとして受け取られ、現在選択されている範囲のアドレスが返されることを前提としています。 完全な例については、 この記事の後半で説明します

    test("getSelectedRangeAddress should return the address of the range", async function () {
      expect(await getSelectedRangeAddress(contextMock)).toBe("C2:G3");
    });
    
  6. テスト フレームワークと開発ツールのドキュメントに従ってテストを実行します。 通常、テスト フレームワークを実行するスクリプトを含む package.json ファイルがあります。 たとえば、Jest がフレームワークの場合、 package.json には次のものが含まれます。

    "scripts": {
      "test": "jest",
      -- Other scripts omitted. --  
    }
    

    テストを実行するには、プロジェクトのルートにあるコマンド プロンプトに次のように入力します。

    npm test
    

このセクションの例では、既定の設定で Jest を使用します。 これらの設定では、CommonJS モジュールがサポートされています。 TypeScript を使用し、ECMAScript モジュールをサポートするように Jest と Node.js を構成する方法については、 概要とECMAScript モジュールに関する Jest ドキュメントを参照してください。

これらの例のいずれかを実行するには、次の手順を実行します。

  1. 適切な Office ホスト アプリケーション (Excel やWordなど) の Office アドイン プロジェクトを作成します。 これを迅速に行う方法の 1 つは、 Office アドイン用の Yeoman ジェネレーターを使用することです。
  2. プロジェクトのルートに Jest をインストールします。
  3. office-addin-mock ツールをインストールします。
  4. 例の最初のファイルとまったく同じようにファイルを作成し、プロジェクトの他のソース ファイル (多くの場合、 \src) を含むフォルダーに追加します。
  5. ソース ファイル フォルダーのサブフォルダーを作成し、 \testsなどの適切な名前を付けます。
  6. 例のテスト ファイルとまったく同じファイルを作成し、サブフォルダーに追加します。
  7. package.json ファイルにtest スクリプトを追加し、「基本的な使用法」で説明されているようにテストを実行します。

Office 共通 API のモック作成

この例では、Office Common API (Excel、PowerPoint、Word など) をサポートするすべてのホストの Office アドインを想定しています。 アドインには、 my-common-api-add-in-feature.js という名前のファイル内の機能の 1 つがあります。 次のコードは、ファイルの内容を示しています。 addHelloWorldText関数は、ドキュメントで現在選択されている内容 (たとえば、Wordの範囲、Excel のセル、PowerPointのテキスト ボックスなど) にテキスト "Hello World!" を設定します。

const myCommonAPIAddinFeature = {

    addHelloWorldText: async () => {
        const options = { coercionType: Office.CoercionType.Text };
        await Office.context.document.setSelectedDataAsync("Hello World!", options);
    }
}
  
module.exports = myCommonAPIAddinFeature;

my-common-api-add-in-feature.test.jsという名前のテスト ファイルは、アドイン コード ファイルの場所を基準にしてサブフォルダー内にあります。 次のコードは、ファイルの内容を示しています。 最上位のプロパティは Office.Context オブジェクトcontextであるため、モックされるオブジェクトは、このプロパティの親である Office オブジェクトです。 このコードについては、次の点に注意してください。

  • OfficeMockObject コンストラクターでは、モック Office オブジェクトにすべての Office 列挙型クラスが追加されるわけではありません。そのため、シード オブジェクトでアドイン メソッドで明示的に参照されるCoercionType.Text値を追加する必要があります。
  • Office JavaScript ライブラリはノード プロセスに読み込まれていないため、アドイン コードで参照される Office オブジェクトを宣言して初期化する必要があります。
const OfficeAddinMock = require("office-addin-mock");
const myCommonAPIAddinFeature = require("../my-common-api-add-in-feature");

// Create the seed mock object.
const mockData = {
    context: {
      document: {
        setSelectedDataAsync: function (data, options) {
          this.data = data;
          this.options = options;
        },
      },
    },
    // Mock the Office.CoercionType enum.
    CoercionType: {
      Text: {},
    },
};
  
// Create the final mock object from the seed object.
const officeMock = new OfficeAddinMock.OfficeMockObject(mockData);

// Create the Office object that is called in the addHelloWorldText function.
global.Office = officeMock;

/* Code that calls the test framework goes below this line. */

// Jest test
test("Text of selection in document should be set to 'Hello World'", async function () {
    await myCommonAPIAddinFeature.addHelloWorldText();
    expect(officeMock.context.document.data).toBe("Hello World!");
});

Outlook API のモック作成

Outlook API は Common API モデルの一部ですが、 Mailbox オブジェクトを中心に構築された特殊なアーキテクチャを備えています。 Outlook の個別の例を示します。 次の使用例は、 my-outlook-add-in-feature.js という名前のファイルに機能の 1 つを含む Outlook アドインを想定しています。 次のコードは、ファイルの内容を示しています。 addHelloWorldText関数は、メッセージ作成ウィンドウで現在選択されている内容にテキスト "Hello World!" を設定します。

const myOutlookAddinFeature = {

    addHelloWorldText: async () => {
        Office.context.mailbox.item.setSelectedDataAsync("Hello World!");
      }
}

module.exports = myOutlookAddinFeature;

my-outlook-add-in-feature.test.jsという名前のテスト ファイルは、アドイン コード ファイルの場所を基準にしたサブフォルダーにあります。 次のコードは、ファイルの内容を示しています。 最上位のプロパティは、Office.Context オブジェクトcontextです。 モックがターゲットとするオブジェクトは、このプロパティの親である Office オブジェクトです。 このコードに関する次の詳細に注意してください。

  • モック オブジェクトの host プロパティは、モック ライブラリによって内部的に使用され、Office アプリケーションを識別します。 Outlook では必須です。 現在、他の Office アプリケーションの目的は果たしません。
  • Office JavaScript ライブラリはノード プロセスに読み込まれていないため、アドイン コードで参照される Office オブジェクトを宣言して初期化する必要があります。
const OfficeAddinMock = require("office-addin-mock");
const myOutlookAddinFeature = require("../my-outlook-add-in-feature");

// Create the seed mock object.
const mockData = {
  // Identify the host to the mock library (required for Outlook).
  host: "outlook",
  context: {
    mailbox: {
      item: {
          setSelectedDataAsync: function (data) {
          this.data = data;
        },
      },
    },
  },
};
  
// Create the final mock object from the seed object.
const officeMock = new OfficeAddinMock.OfficeMockObject(mockData);

// Create the Office object that is called in the addHelloWorldText function.
global.Office = officeMock;

/* Code that calls the test framework goes below this line. */

// Jest test
test("Text of selection in message should be set to 'Hello World'", async function () {
    await myOutlookAddinFeature.addHelloWorldText();
    expect(officeMock.context.mailbox.item.data).toBe("Hello World!");
});

Office アプリケーション固有の API をモックする

アプリケーション固有の API を使用する関数をテストする場合は、適切な種類のオブジェクトをモックします。 2 つのオプションがあります。

  • OfficeExtension.ClientRequestObject をモックします。 テストする関数が次の両方の条件を満たしている場合は、このオプションを使用します。

    • ホストは呼び出しません。 run 関数 ( Excel.run など)。
    • Host オブジェクトの他の直接プロパティまたはメソッドは参照しません。
  • ExcelWord などの Host オブジェクトをモックします。 上記のオプションが使用できない場合は、このオプションを使用します。

次のサブセクションでは、両方の種類のテストの例を示します。

注:

Office-Addin-Mock ライブラリでは、現在、コレクション型オブジェクトのモックはサポートされていません。 これらのオブジェクトは、WorksheetCollection などの コレクション の名前付けパターンに従う、アプリケーション固有の API 内のすべてのオブジェクトです。 このサポートをライブラリに追加するために一生懸命取り組んでいます。

ClientRequestContext オブジェクトをモックする

次の使用例は、 my-excel-add-in-feature.js という名前のファイルに機能の 1 つを含む Excel アドインを想定しています。 次のコードは、ファイルの内容を示しています。 getSelectedRangeAddress関数は、コールバック内で呼び出されたヘルパー メソッドであり、Excel.runに渡されることに注意してください。

const myExcelAddinFeature = {
    
    getSelectedRangeAddress: async (context) => {
        const range = context.workbook.getSelectedRange();      
        range.load("address");

        await context.sync();
      
        return range.address;
    }
}

module.exports = myExcelAddinFeature;

my-excel-add-in-feature.test.jsという名前のテスト ファイルは、アドイン コード ファイルの場所を基準にしたサブフォルダーにあります。 次のコードは、ファイルの内容を示しています。 最上位のプロパティは workbookであるため、モックされるオブジェクトは、 Excel.Workbookの親である ClientRequestContext オブジェクトです。

const OfficeAddinMock = require("office-addin-mock");
const myExcelAddinFeature = require("../my-excel-add-in-feature");

// Create the seed mock object.
const mockData = {
    workbook: {
      range: {
        address: "C2:G3",
      },
      // Mock the Workbook.getSelectedRange method.
      getSelectedRange: function () {
        return this.range;
      },
    },
};

// Create the final mock object from the seed object.
const contextMock = new OfficeAddinMock.OfficeMockObject(mockData);

/* Code that calls the test framework goes below this line. */

// Jest test
test("getSelectedRangeAddress should return address of selected range", async function () {
  expect(await myOfficeAddinFeature.getSelectedRangeAddress(contextMock)).toBe("C2:G3");
});

ホスト オブジェクトのモック化

この例では、my-word-add-in-feature.js という名前のファイルに機能の 1 つを含むWord アドインを想定しています。 次のコードは、ファイルの内容を示しています。

const myWordAddinFeature = {

  insertBlueParagraph: async () => {
    return Word.run(async (context) => {
      // Insert a paragraph at the end of the document.
      const paragraph = context.document.body.insertParagraph("Hello World", Word.InsertLocation.end);
  
      // Change the font color to blue.
      paragraph.font.color = "blue";
  
      await context.sync();
    });
  }
}

module.exports = myWordAddinFeature;

my-word-add-in-feature.test.jsという名前のテスト ファイルは、アドイン コード ファイルの場所を基準にしたサブフォルダーにあります。 次のコードは、ファイルの内容を示しています。 最上位のプロパティは contextClientRequestContext オブジェクトであるため、モックされるオブジェクトは、このプロパティの親である Word オブジェクトです。 このコードに関する次の詳細に注意してください。

  • OfficeMockObject コンストラクターが最終的なモック オブジェクトを作成すると、子ClientRequestContext オブジェクトに sync メソッドと load メソッドが確実に含まれます。
  • OfficeMockObject コンストラクターはモック Word オブジェクトにrun関数を追加しないため、シード オブジェクトに明示的に追加する必要があります。
  • OfficeMockObject コンストラクターでは、モック Word オブジェクトにすべてのWord列挙型クラスが追加されるわけではありません。そのため、シード オブジェクトでアドイン メソッドで明示的に参照されるInsertLocation.end値を追加する必要があります。
  • Office JavaScript ライブラリはノード プロセスに読み込まれていないため、アドイン コードで参照されている Word オブジェクトを宣言して初期化する必要があります。
const OfficeAddinMock = require("office-addin-mock");
const myWordAddinFeature = require("../my-word-add-in-feature");

// Create the seed mock object.
const mockData = {
  context: {
    document: {
      body: {
        paragraph: {
          font: {},
        },
        // Mock the Body.insertParagraph method.
        insertParagraph: function (paragraphText, insertLocation) {
          this.paragraph.text = paragraphText;
          this.paragraph.insertLocation = insertLocation;
          return this.paragraph;
        },
      },
    },
  },
  // Mock the Word.InsertLocation enum.
  InsertLocation: {
    end: "end",
  },
  // Mock the Word.run function.
  run: async function(callback) {
    await callback(this.context);
  },
};

// Create the final mock object from the seed object.
const wordMock = new OfficeAddinMock.OfficeMockObject(mockData);

// Define and initialize the Word object that is called in the insertBlueParagraph function.
global.Word = wordMock;

/* Code that calls the test framework goes below this line. */

// Jest test set
describe("Insert blue paragraph at end tests", () => {

  test("color of paragraph", async function () {
    await myWordAddinFeature.insertBlueParagraph();  
    expect(wordMock.context.document.body.paragraph.font.color).toBe("blue");
  });

  test("text of paragraph", async function () {
    await myWordAddinFeature.insertBlueParagraph();
    expect(wordMock.context.document.body.paragraph.text).toBe("Hello World");
  });
})

注:

OfficeMockObject型の完全なリファレンス ドキュメントは、Office-Addin-Mock にあります

関連項目