Teste de unidade em suplementos do Office

Os testes de unidade marcar a funcionalidade do suplemento sem exigir conexões de rede ou serviço, incluindo conexões com o aplicativo do Office. O código do lado do servidor de teste de unidade e o código do lado do cliente que não chama as APIs JavaScript do Office, é o mesmo em Suplementos do Office como em qualquer aplicativo Web, portanto, não requer nenhuma documentação especial. Mas o código do lado do cliente que chama as APIs JavaScript do Office é um desafio para testar. Para resolver esses problemas, criamos uma biblioteca para simplificar a criação de objetos simulados do Office em testes de unidade: Office-Addin-Mock. A biblioteca facilita o teste das seguintes maneiras:

  • As APIs JavaScript do Office devem inicializar em um controle webview no contexto de um aplicativo do Office (Excel, Word etc.), para que não possam ser carregadas no processo no qual os testes de unidade são executados no computador de desenvolvimento. A biblioteca Office-Addin-Mock pode ser importada para seus arquivos de teste, o que permite a zombaria das APIs JavaScript do Office dentro do processo de Node.js em que os testes são executados.
  • As APIs específicas do aplicativo têm métodos de carregamento e sincronização que devem ser chamados em uma ordem específica em relação a outras funções e entre si. Além disso, o load método deve ser chamado com determinados parâmetros, dependendo de quais propriedades dos objetos do Office serão lidas por código posteriormente na função que está sendo testada. Mas as estruturas de teste de unidade são inerentemente sem estado, portanto, não podem manter um registro de se load ou sync foi chamado ou quais parâmetros foram passados para load. Os objetos simulados que você cria com a biblioteca do Office-Addin-Mock têm estado interno que mantém o controle dessas coisas. Isso permite que os objetos simulados emulem o comportamento de erro de objetos do Office reais. Por exemplo, se a função que está sendo testada tentar ler uma propriedade que não foi passada primeiro para load, o teste retornará um erro semelhante ao que o Office retornaria.

A biblioteca não depende das APIs JavaScript do Office e pode ser usada com qualquer estrutura de teste de unidade JavaScript, como:

Os exemplos neste artigo usam a estrutura Jest. Há exemplos usando a estrutura do Mocha na página inicial do Office-Addin-Mock.

Pré-requisitos

Este artigo pressupõe que você esteja familiarizado com os conceitos básicos de teste de unidade e zombaria, incluindo como criar e executar arquivos de teste e que você tenha alguma experiência com uma estrutura de teste de unidade.

Dica

Se você estiver trabalhando com o Visual Studio, recomendamos ler o artigo Unidade testando JavaScript e TypeScript no Visual Studio para obter algumas informações básicas sobre o teste de unidade JavaScript no Visual Studio e, em seguida, retornar a este artigo.

Instalar a ferramenta

Para instalar a biblioteca, abra um prompt de comando, navegue até a raiz do projeto de suplemento e insira o comando a seguir.

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

Uso básico

  1. Seu projeto terá um ou mais arquivos de teste. (Confira as instruções para sua estrutura de teste e os arquivos de teste de exemplo em Exemplos abaixo.) Importe a biblioteca, com o require ou import palavra-chave, para qualquer arquivo de teste que tenha um teste de uma função que chame as APIs JavaScript do Office, conforme mostrado no exemplo a seguir.

    const OfficeAddinMock = require("office-addin-mock");
    
  2. Importe o módulo que contém a função de suplemento que você deseja testar com o require ou import palavra-chave. A seguir está um exemplo que pressupõe que seu arquivo de teste esteja em uma subpasta da pasta com os arquivos de código do suplemento.

    const myOfficeAddinFeature = require("../my-office-add-in");
    
  3. Crie um objeto de dados que tenha as propriedades e subpropertas que você precisa simular para testar a função. A seguir está um exemplo de um objeto que simula a propriedade Excel Workbook.range.address e o método Workbook.getSelectedRange . Este não é o objeto simulado final. Pense nele como um objeto semente usado por OfficeMockObject para criar o objeto simulado final.

    const mockData = {
      workbook: {
        range: {
          address: "C2:G3",
        },
        getSelectedRange: function () {
          return this.range;
        },
      },
    };
    
  4. Passe o objeto de dados para o OfficeMockObject construtor. Observe o seguinte sobre o objeto retornado OfficeMockObject .

    • É uma simulação simplificada de um objeto OfficeExtension.ClientRequestContext .
    • O objeto simulado tem todos os membros do objeto de dados e também tem implementações simuladas dos load métodos e sync .
    • O objeto simulado imitará o comportamento de erro crucial do ClientRequestContext objeto. Por exemplo, se a API do Office que você está testando tentar ler uma propriedade sem primeiro carregar a propriedade e chamar sync, o teste falhará com um erro semelhante ao que seria lançado no runtime de produção: "Erro, propriedade não carregada".
    const contextMock = new OfficeAddinMock.OfficeMockObject(mockData);
    

    Observação

    A documentação de referência completa para o OfficeMockObject tipo está no Office-Addin-Mock.

  5. Na sintaxe da estrutura de teste, adicione um teste da função. Use o OfficeMockObject objeto no lugar do objeto que ele simula, nesse caso o ClientRequestContext objeto. O seguinte continua o exemplo em Jest. Este teste de exemplo pressupõe que a função de suplemento que está sendo testada é chamada getSelectedRangeAddress, que ele usa um ClientRequestContext objeto como parâmetro e que se destina a retornar o endereço do intervalo selecionado atualmente. O exemplo completo é posterior neste artigo.

    test("getSelectedRangeAddress should return the address of the range", async function () {
      expect(await getSelectedRangeAddress(contextMock)).toBe("C2:G3");
    });
    
  6. Execute o teste de acordo com a documentação da estrutura de teste e suas ferramentas de desenvolvimento. Normalmente, há um arquivo package.json com um script que executa a estrutura de teste. Por exemplo, se Jest for a estrutura, package.json conterá o seguinte:

    "scripts": {
      "test": "jest",
      -- other scripts omitted --  
    }
    

    Para executar o teste, insira o seguinte em um prompt de comando na raiz do projeto.

    npm test
    

Exemplos

Os exemplos nesta seção usam Jest com suas configurações padrão. Essas configurações dão suporte a módulos CommonJS. Consulte a documentação Jest para saber como configurar jest e Node.js para dar suporte a módulos ECMAScript e dar suporte ao TypeScript. Para executar qualquer um desses exemplos, siga as etapas a seguir.

  1. Crie um projeto de suplemento do Office para o aplicativo host do Office apropriado (por exemplo, Excel ou Word). Uma maneira de fazer isso rapidamente é usar o gerador Yeoman para suplementos do Office.
  2. Na raiz do projeto, instale jest.
  3. Instale a ferramenta office-addin-mock.
  4. Crie um arquivo exatamente como o primeiro arquivo no exemplo e adicione-o à pasta que contém outros arquivos de origem do projeto, muitas vezes chamados \src.
  5. Crie uma subpasta para a pasta de arquivo de origem e dê a ela um nome apropriado, como \tests.
  6. Crie um arquivo exatamente como o arquivo de teste no exemplo e adicione-o à subpasta.
  7. Adicione um test script ao arquivo package.json e execute o teste, conforme descrito no uso básico.

Zombando das APIs Comuns do Office

Este exemplo pressupõe um Suplemento do Office para qualquer host que dê suporte às APIs Comuns do Office (por exemplo, Excel, PowerPoint ou Word). O suplemento tem um de seus recursos em um arquivo chamado my-common-api-add-in-feature.js. O seguinte mostra o conteúdo do arquivo. A addHelloWorldText função define o texto "Olá, Mundo!" como o que está selecionado atualmente no documento; por exemplo, um intervalo em Word ou uma célula no Excel ou uma caixa de texto no PowerPoint.

const myCommonAPIAddinFeature = {

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

O arquivo de teste, nomeado my-common-api-add-in-feature.test.js está em uma subpasta, em relação ao local do arquivo de código de suplemento. O seguinte mostra o conteúdo do arquivo. Observe que a propriedade de nível superior é context, um objeto Office.Context , portanto, o objeto que está sendo ridicularizado é o pai desta propriedade: um objeto do Office . Observe o seguinte sobre este código:

  • O OfficeMockObject construtor não adiciona todas as classes de enumeração do Office ao objeto simulado Office , portanto, o CoercionType.Text valor referenciado no método de suplemento deve ser adicionado explicitamente no objeto semente.
  • Como a biblioteca JavaScript do Office não é carregada no processo de nó, o Office objeto referenciado no código de suplemento deve ser declarado e inicializado.
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!");
});

Zombando das APIs do Outlook

Embora estritamente falando, as APIs do Outlook fazem parte do modelo de API Comum, elas têm uma arquitetura especial criada em torno do objeto Mailbox , portanto, fornecemos um exemplo distinto para o Outlook. Este exemplo pressupõe um Outlook que tenha um de seus recursos em um arquivo chamado my-outlook-add-in-feature.js. O seguinte mostra o conteúdo do arquivo. A addHelloWorldText função define o texto "Olá, Mundo!" como o que está selecionado no momento na janela de composição da mensagem.

const myOutlookAddinFeature = {

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

module.exports = myOutlookAddinFeature;

O arquivo de teste, nomeado my-outlook-add-in-feature.test.js está em uma subpasta, em relação ao local do arquivo de código de suplemento. O seguinte mostra o conteúdo do arquivo. Observe que a propriedade de nível superior é context, um objeto Office.Context , portanto, o objeto que está sendo ridicularizado é o pai desta propriedade: um objeto do Office . Observe o seguinte sobre este código:

  • A host propriedade no objeto mock é usada internamente pela biblioteca simulada para identificar o aplicativo do Office. É obrigatório para o Outlook. Atualmente, ele não serve para nenhum outro aplicativo do Office.
  • Como a biblioteca JavaScript do Office não é carregada no processo de nó, o Office objeto referenciado no código de suplemento deve ser declarado e inicializado.
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!");
});

Zombando das APIs específicas do aplicativo do Office

Quando estiver testando funções que usam as APIs específicas do aplicativo, certifique-se de que você está zombando do tipo certo de objeto. Há duas opções:

  • Simular um OfficeExtension.ClientRequestObject. Faça isso quando a função que está sendo testada atender às duas condições a seguir:

    • Ele não chama um Host.run função, como Excel.run.
    • Ele não faz referência a nenhuma outra propriedade direta ou método de um objeto Host .
  • Simular um objeto Host, como Excel ou Word. Faça isso quando a opção anterior não for possível.

Exemplos de ambos os tipos de testes estão nas subseções abaixo.

Observação

A biblioteca Office-Addin-Mock atualmente não dá suporte a objetos de tipo de coleção zombando, que são todos os objetos nas APIs específicas do aplicativo que são nomeadas no padrão *Collection, como WorksheetCollection. Estamos trabalhando duro para adicionar esse suporte à biblioteca.

Zombando de um objeto ClientRequestContext

Este exemplo pressupõe um suplemento do Excel que tenha um de seus recursos em um arquivo chamado my-excel-add-in-feature.js. O seguinte mostra o conteúdo do arquivo. Observe que o getSelectedRangeAddress é um método auxiliar chamado dentro do retorno de chamada que é passado para Excel.run.

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

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

module.exports = myExcelAddinFeature;

O arquivo de teste, nomeado my-excel-add-in-feature.test.js está em uma subpasta, em relação ao local do arquivo de código de suplemento. O seguinte mostra o conteúdo do arquivo. Observe que a propriedade de nível superior é workbook, portanto, o objeto que está sendo ridicularizado é o pai de um Excel.Workbook: um ClientRequestContext objeto.

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

Zombando de um objeto host

Este exemplo pressupõe um suplemento Word que tenha um de seus recursos em um arquivo chamado my-word-add-in-feature.js. O seguinte mostra o conteúdo do arquivo.

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;

O arquivo de teste, nomeado my-word-add-in-feature.test.js está em uma subpasta, em relação ao local do arquivo de código de suplemento. O seguinte mostra o conteúdo do arquivo. Observe que a propriedade de nível superior é context, um ClientRequestContext objeto, portanto, o objeto que está sendo ridicularizado é o pai desta propriedade: um Word objeto. Observe o seguinte sobre este código:

  • Quando o OfficeMockObject construtor criar o objeto simulado final, ele garantirá que o objeto filho ClientRequestContext tenha sync e load métodos.
  • O OfficeMockObject construtor não adiciona uma run função ao objeto simulado Word , portanto, ele deve ser adicionado explicitamente no objeto semente.
  • O OfficeMockObject construtor não adiciona todas as classes de enumeração Word ao objeto mockWord, portanto, o InsertLocation.end valor referenciado no método de suplemento deve ser adicionado explicitamente no objeto semente.
  • Como a biblioteca JavaScript do Office não é carregada no processo de nó, o Word objeto referenciado no código de suplemento deve ser declarado e inicializado.
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");
  });
})

Observação

A documentação de referência completa para o OfficeMockObject tipo está no Office-Addin-Mock.

Confira também