Tutorial: Adicionar testes de unidade a projetos de visual do Power BITutorial: Add unit tests for Power BI visual projects

Este artigo descreve as noções básicas de como escrever testes de unidade para seus visuais do Power BI, incluindo como:This article describes the basics of writing unit tests for your Power BI visuals, including how to:

  • Configurar a estrutura de teste do executor de teste do Karma JavaScript, Jasmine.Set up the Karma JavaScript test runner testing framework, Jasmine.
  • Usar o pacote powerbi-visuals-utils-testutils.Use the powerbi-visuals-utils-testutils package.
  • Usar simulações e elementos fictícios para ajudar a simplificar o teste de unidade de visuais do Power BI.Use mocks and fakes to help simplify unit testing of Power BI visuals.

Pré-requisitosPrerequisites

  • Um projeto de visuais do Power BI instaladoAn installed Power BI visuals project
  • Um ambiente do Node.js configuradoA configured Node.js environment

Instalar e configurar o executor de teste do Karma JavaScript e o JasmineInstall and configure the Karma JavaScript test runner and Jasmine

Adicione as bibliotecas necessárias ao arquivo package.json na seção devDependencies:Add the required libraries to the package.json file in the devDependencies section:

"@babel/polyfill": "^7.2.5",
"@types/d3": "5.5.0",
"@types/jasmine": "2.5.37",
"@types/jasmine-jquery": "1.5.28",
"@types/jquery": "2.0.41",
"@types/karma": "3.0.0",
"@types/lodash-es": "4.17.1",
"coveralls": "3.0.2",
"istanbul-instrumenter-loader": "^3.0.1",
"jasmine": "2.5.2",
"jasmine-core": "2.5.2",
"jasmine-jquery": "2.1.1",
"jquery": "3.1.1",
"karma": "3.1.1",
"karma-chrome-launcher": "2.2.0",
"karma-coverage": "1.1.2",
"karma-coverage-istanbul-reporter": "^2.0.4",
"karma-jasmine": "2.0.1",
"karma-junit-reporter": "^1.2.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-typescript": "^3.0.13",
"karma-typescript-preprocessor": "0.4.0",
"karma-webpack": "3.0.5",
"puppeteer": "1.17.0",
"style-loader": "0.23.1",
"ts-loader": "5.3.0",
"ts-node": "7.0.1",
"tslint": "^5.12.0",
"webpack": "4.26.0"

Para saber mais sobre package.json, consulte a descrição em npm-package.json.To learn more about package.json, see the description at npm-package.json.

Salve o arquivo package.json e, na localização package.json, execute o seguinte comando:Save the package.json file and, at the package.json location, run the following command:

npm install

O gerenciador de pacotes instala todos os novos pacotes que são adicionados ao package.json.The package manager installs all new packages that are added to package.json.

Para executar testes de unidade, configure o executor e a configuração webpack.To run unit tests, configure the test runner and webpack config.

O código a seguir é um exemplo do arquivo test.webpack.config.js:The following code is a sample of the test.webpack.config.js file:

const path = require('path');
const webpack = require("webpack");

module.exports = {
    devtool: 'source-map',
    mode: 'development',
    optimization : {
        concatenateModules: false,
        minimize: false
    },
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: 'ts-loader',
                exclude: /node_modules/
            },
            {
                test: /\.json$/,
                loader: 'json-loader'
            },
            {
                test: /\.tsx?$/i,
                enforce: 'post',
                include: /(src)/,
                exclude: /(node_modules|resources\/js\/vendor)/,
                loader: 'istanbul-instrumenter-loader',
                options: { esModules: true }
            },
            {
                test: /\.less$/,
                use: [
                    {
                        loader: 'style-loader'
                    },
                    {
                        loader: 'css-loader'
                    },
                    {
                        loader: 'less-loader',
                        options: {
                            paths: [path.resolve(__dirname, 'node_modules')]
                        }
                    }
                ]
            }
        ]
    },
    externals: {
        "powerbi-visuals-api": '{}'
    },
    resolve: {
        extensions: ['.tsx', '.ts', '.js', '.css']
    },
    output: {
        path: path.resolve(__dirname, ".tmp/test")
    },
    plugins: [
        new webpack.ProvidePlugin({
            'powerbi-visuals-api': null
        })
    ]
};

O código a seguir é um exemplo do arquivo karma.conf.ts:The following code is a sample of the karma.conf.ts file:

"use strict";

const webpackConfig = require("./test.webpack.config.js");
const tsconfig = require("./test.tsconfig.json");
const path = require("path");

const testRecursivePath = "test/visualTest.ts";
const srcOriginalRecursivePath = "src/**/*.ts";
const coverageFolder = "coverage";

process.env.CHROME_BIN = require("puppeteer").executablePath();

import { Config, ConfigOptions } from "karma";

module.exports = (config: Config) => {
    config.set(<ConfigOptions>{
        mode: "development",
        browserNoActivityTimeout: 100000,
        browsers: ["ChromeHeadless"], // or Chrome to use locally installed Chrome browser
        colors: true,
        frameworks: ["jasmine"],
        reporters: [
            "progress",
            "junit",
            "coverage-istanbul"
        ],
        junitReporter: {
            outputDir: path.join(__dirname, coverageFolder),
            outputFile: "TESTS-report.xml",
            useBrowserName: false
        },
        singleRun: true,
        plugins: [
            "karma-coverage",
            "karma-typescript",
            "karma-webpack",
            "karma-jasmine",
            "karma-sourcemap-loader",
            "karma-chrome-launcher",
            "karma-junit-reporter",
            "karma-coverage-istanbul-reporter"
        ],
        files: [
            "node_modules/jquery/dist/jquery.min.js",
            "node_modules/jasmine-jquery/lib/jasmine-jquery.js",
            {
                pattern: './capabilities.json',
                watched: false,
                served: true,
                included: false
            },
            testRecursivePath,
            {
                pattern: srcOriginalRecursivePath,
                included: false,
                served: true
            }
        ],
        preprocessors: {
            [testRecursivePath]: ["webpack", "coverage"]
        },
        typescriptPreprocessor: {
            options: tsconfig.compilerOptions
        },
        coverageIstanbulReporter: {
            reports: ["html", "lcovonly", "text-summary", "cobertura"],
            dir: path.join(__dirname, coverageFolder),
            'report-config': {
                html: {
                    subdir: 'html-report'
                }
            },
            combineBrowserReports: true,
            fixWebpackSourcePaths: true,
            verbose: false
        },
        coverageReporter: {
            dir: path.join(__dirname, coverageFolder),
            reporters: [
                // reporters not supporting the `file` property
                { type: 'html', subdir: 'html-report' },
                { type: 'lcov', subdir: 'lcov' },
                // reporters supporting the `file` property, use `subdir` to directly
                // output them in the `dir` directory
                { type: 'cobertura', subdir: '.', file: 'cobertura-coverage.xml' },
                { type: 'lcovonly', subdir: '.', file: 'report-lcovonly.txt' },
                { type: 'text-summary', subdir: '.', file: 'text-summary.txt' },
            ]
        },
        mime: {
            "text/x-typescript": ["ts", "tsx"]
        },
        webpack: webpackConfig,
        webpackMiddleware: {
            stats: "errors-only"
        }
    });
};

Se necessário, você pode modificar essa configuração.If necessary, you can modify this configuration.

O código em karma.conf.js contém a seguinte variável:The code in karma.conf.js contains the following variable:

  • recursivePathToTests: Localiza o código de testerecursivePathToTests: Locates the test code

  • srcRecursivePath: Localiza o código JavaScript de saída após a compilaçãosrcRecursivePath: Locates the output JavaScript code after compiling

  • srcCssRecursivePath: Localiza o CSS de saída depois de compilar menos arquivo com estilossrcCssRecursivePath: Locates the output CSS after compiling less file with styles

  • srcOriginalRecursivePath: Localiza o código-fonte do seu visualsrcOriginalRecursivePath: Locates the source code of your visual

  • coverageFolder: Determina o local em que o relatório de cobertura deve ser criadocoverageFolder: Determines where the coverage report is to be created

O arquivo de configuração inclui as seguintes propriedades:The configuration file includes the following properties:

  • singleRun: true: Os testes são executados em um sistema de CI (integração contínua) ou podem ser executados uma vez.singleRun: true: Tests are run on a continuous integration (CI) system, or they can be run one time. Você pode alterar a configuração para false para depurar seus testes.You can change the setting to false for debugging your tests. O Karma mantém o navegador em execução para que você possa usar o console para depuração.Karma keeps the browser running so that you can use the console for debugging.

  • files: [...]: Nessa matriz, você pode especificar os arquivos a serem carregados no navegador.files: [...]: In this array, you can specify the files to load to the browser. Normalmente, há arquivos de origem, casos de teste, bibliotecas (Jasmine, utilitários de teste).Usually, there are source files, test cases, libraries (Jasmine, test utilities). Você pode adicionar outros arquivos à lista, conforme necessário.You can add additional files to the list, as necessary.

  • preprocessors: Nesta seção, você configurará ações executadas antes da execução dos testes de unidade.preprocessors: In this section, you configure actions that run before the unit tests run. Eles preparam o typescript para JavaScript, preparam os arquivos de source map e geram o relatório de cobertura de código.They precompile the typescript to JavaScript, prepare source map files, and generate code coverage report. Você pode desabilitar coverage ao depurar seus testes.You can disable coverage when you debug your tests. A cobertura gera código adicional para verificar o código para a cobertura de teste, o que complica a depuração dos testes.Coverage generates additional code for check code for the test coverage, which complicates debugging tests.

Para obter descrições de todas as configurações do karma, vá para a página Arquivo de Configuração do Karma.For descriptions of all Karma configurations, go to the Karma Configuration File page.

Para sua conveniência, você pode adicionar um comando de teste em scripts:For your convenience, you can add a test command into scripts:

{
    "scripts": {
        "pbiviz": "pbiviz",
        "start": "pbiviz start",
        "typings":"node node_modules/typings/dist/bin.js i",
        "lint": "tslint -r \"node_modules/tslint-microsoft-contrib\"  \"+(src|test)/**/*.ts\"",
        "pretest": "pbiviz package --resources --no-minify --no-pbiviz --no-plugin",
        "test": "karma start"
    }
    ...
}

Agora você está pronto para começar a escrever seus testes de unidade.You're now ready to begin writing your unit tests.

Verificar o elemento DOM do visualCheck the DOM element of the visual

Para testar o Visual, primeiro crie uma instância do visual.To test the visual, first create an instance of visual.

Criar um construtor de instância do visualCreate a visual instance builder

Adicione um arquivo visualBuilder.ts à pasta de test usando o seguinte código:Add a visualBuilder.ts file to the test folder by using the following code:

import {
    VisualBuilderBase
} from "powerbi-visuals-utils-testutils";

import {
    BarChart as VisualClass
} from "../src/visual";

import  powerbi from "powerbi-visuals-api";
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;

export class BarChartBuilder extends VisualBuilderBase<VisualClass> {
    constructor(width: number, height: number) {
        super(width, height);
    }

    protected build(options: VisualConstructorOptions) {
        return new VisualClass(options);
    }

    public get mainElement() {
        return this.element.children("svg.barChart");
    }
}

Há um método build para criar uma instância do seu visual.There's build method for creating an instance of your visual. mainElement é um método Get, que retorna uma instância do elemento DOM (Modelo de Objeto do Documento) de "raiz" em seu visual.mainElement is a get method, which returns an instance of "root" document object model (DOM) element in your visual. O getter é opcional, mas facilita a gravação do teste de unidade.The getter is optional, but it makes writing the unit test easier.

Agora você tem um build de uma instância do seu visual.You now have a build of an instance of your visual. Vamos escrever o caso de teste.Let's write the test case. O caso de teste verifica os elementos SVG criados quando o visual é exibido.The test case checks the SVG elements that are created when your visual is displayed.

Criar um arquivo typescript para escrever casos de testeCreate a typescript file to write test cases

Adicione um arquivo visualTest.ts para os casos de teste usando o seguinte código:Add a visualTest.ts file for the test cases by using the following code:

import powerbi from "powerbi-visuals-api";

import { BarChartBuilder } from "./VisualBuilder";

import {
    BarChart as VisualClass
} from "../src/visual";

import VisualBuilder = powerbi.extensibility.visual.test.BarChartBuilder;

describe("BarChart", () => {
    let visualBuilder: VisualBuilder;
    let dataView: DataView;

    beforeEach(() => {
        visualBuilder = new VisualBuilder(500, 500);
    });

    it("root DOM element is created", () => {
        expect(visualBuilder.mainElement).toBeInDOM();
    });
});

Vários métodos são chamados:Several methods are called:

  • describe: Descreve um caso de teste.describe: Describes a test case. No contexto da estrutura do Jasmine, geralmente descreve um conjunto ou grupo de especificações.In the context of the Jasmine framework, it often describes a suite or group of specs.

  • beforeEach: É chamado antes de cada chamada do método it, que é definida no método describe.beforeEach: Is called before each call of the it method, which is defined in the describe method.

  • it: Define uma única especificação. O método it deve conter um ou mais expectations.it: Defines a single spec. The it method should contain one or more expectations.

  • expect: Cria uma expectativa para uma especificação. Uma especificação tem êxito se todas as expectativas são aprovadas sem falhas.expect: Creates an expectation for a spec. A spec succeeds if all expectations pass without any failures.

  • toBeInDOM: Um dos métodos de correspondências.toBeInDOM: One of the matchers methods. Para obter mais informações sobre correspondências, confira Namespace do Jasmine: correspondências.For more information about matchers, see Jasmine Namespace: matchers.

Para obter mais informações sobre o Jasmine, confira a página Documentação da estrutura do Jasmine.For more information about Jasmine, see the Jasmine framework documentation page.

Iniciar testes de unidadeLaunch unit tests

Esse teste verifica se o elemento SVG raiz dos visuais é criado.This test checks that root SVG element of the visuals is created. Para executar o teste de unidade, digite o seguinte comando na ferramenta de linha de comando:To run the unit test, enter the following command in the command-line tool:

npm run test

karma.js executa o caso de teste no navegador Chrome.karma.js runs the test case in the Chrome browser.

Karma JavaScript aberto no Chrome

Observação

Você deve instalar o Google Chrome localmente.You must install Google Chrome locally.

Na janela de linha de comando, você obterá a seguinte saída:In the command-line window, you'll get following output:

> karma start

23 05 2017 12:24:26.842:WARN [watcher]: Pattern "E:/WORKSPACE/PowerBI/PowerBI-visuals-sampleBarChart/data/*.csv" does not match any file.
23 05 2017 12:24:30.836:WARN [karma]: No captured browser, open https://localhost:9876/
23 05 2017 12:24:30.849:INFO [karma]: Karma v1.3.0 server started at https://localhost:9876/
23 05 2017 12:24:30.850:INFO [launcher]: Launching browser Chrome with unlimited concurrency
23 05 2017 12:24:31.059:INFO [launcher]: Starting browser Chrome
23 05 2017 12:24:33.160:INFO [Chrome 58.0.3029 (Windows 10 0.0.0)]: Connected on socket /#2meR6hjXFmsE_fjiAAAA with id 5875251
Chrome 58.0.3029 (Windows 10 0.0.0): Executed 1 of 1 SUCCESS (0.194 secs / 0.011 secs)

=============================== Coverage summary ===============================
Statements   : 27.43% ( 65/237 )
Branches     : 19.84% ( 25/126 )
Functions    : 43.86% ( 25/57 )
Lines        : 20.85% ( 44/211 )
================================================================================

Como adicionar dados estáticos para testes de unidadeHow to add static data for unit tests

Crie o arquivo visualData.ts na pasta test usando o seguinte código:Create the visualData.ts file in the test folder by using the following code:

import powerbi from "powerbi-visuals-api";
import DataView = powerbi.DataView;

import {
    testDataViewBuilder,
    getRandomNumbers
} from "powerbi-visuals-utils-testutils";

export class SampleBarChartDataBuilder extends TestDataViewBuilder {
    public static CategoryColumn: string = "category";
    public static MeasureColumn: string = "measure";

    public constructor() {
        super();
        ...
    }

    public getDataView(columnNames?: string[]): DataView {
        let dateView: any = this.createCategoricalDataViewBuilder([
            ...
        ],
        [
            ...
        ], columnNames).build();

        // there's client side computed maxValue
        let maxLocal = 0;
        this.valuesMeasure.forEach((item) => {
                if (item > maxLocal) {
                    maxLocal = item;
                }
        });
        (<any>dataView).categorical.values[0].maxLocal = maxLocal;
    }
}

A classe SampleBarChartDataBuilder estende TestDataViewBuilder e implementa o método abstrato getDataView.The SampleBarChartDataBuilder class extends TestDataViewBuilder and implements the abstract method getDataView.

Quando você coloca dados em buckets de campos de dados, o Power BI produz um objeto categórico dataview baseado em seus dados.When you put data into data-field buckets, Power BI produces a categorical dataview object that's based on your data.

Buckets de campo de dados

Em testes de unidade, você não tem funções principais do Power BI para reproduzir os dados.In unit tests, you don't have Power BI core functions to reproduce the data. Mas você precisa mapear seus dados estáticos para dataview categóricos.But you need to map your static data to the categorical dataview. A TestDataViewBuilder classe pode ajudá-lo a mapeá-la.The TestDataViewBuilder class can help you map it.

Para obter mais informações sobre mapeamento de Exibição de Dados, confira DataViewMappings.For more information about Data View mapping, see DataViewMappings.

No método getDataView, você chama o método createCategoricalDataViewBuilder com seus dados.In the getDataView method, you call the createCategoricalDataViewBuilder method with your data.

No arquivo capabilities.json do visual sampleBarChart, temos objetos dataRoles e dataViewMapping:In sampleBarChart visual capabilities.json file, we have dataRoles and dataViewMapping objects:

"dataRoles": [
    {
        "displayName": "Category Data",
        "name": "category",
        "kind": "Grouping"
    },
    {
        "displayName": "Measure Data",
        "name": "measure",
        "kind": "Measure"
    }
],
"dataViewMappings": [
    {
        "conditions": [
            {
                "category": {
                    "max": 1
                },
                "measure": {
                    "max": 1
                }
            }
        ],
        "categorical": {
            "categories": {
                "for": {
                    "in": "category"
                }
            },
            "values": {
                "select": [
                    {
                        "bind": {
                            "to": "measure"
                        }
                    }
                ]
            }
        }
    }
],

Para gerar o mesmo mapeamento, você deve definir os seguintes parâmetros para o método createCategoricalDataViewBuilder:To generate the same mapping, you must set the following params to createCategoricalDataViewBuilder method:

([
    {
        source: {
            displayName: "Category",
            queryName: SampleBarChartData.ColumnCategory,
            type: ValueType.fromDescriptor({ text: true }),
            roles: {
                Category: true
            },
        },
        values: this.valuesCategory
    }
],
[
    {
        source: {
            displayName: "Measure",
            isMeasure: true,
            queryName: SampleBarChartData.MeasureColumn,
            type: ValueType.fromDescriptor({ numeric: true }),
            roles: {
                Measure: true
            },
        },
        values: this.valuesMeasure
    },
], columnNames)

Em que this.valuesCategory é uma matriz de categorias:Where this.valuesCategory is an array of categories:

public valuesCategory: string[] = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];

E this.valuesMeasure é uma matriz de medidas para cada categoria:And this.valuesMeasure is an array of measures for each category:

public valuesMeasure: number[] = [742731.43, 162066.43, 283085.78, 300263.49, 376074.57, 814724.34, 570921.34];

agora você pode usar a classe SampleBarChartDataBuilder em seu teste de unidade.Now, you can use the SampleBarChartDataBuilder class in your unit test.

A ValueType classe é definida no pacote powerbi-visuals-utils-testutils.The ValueType class is defined in the powerbi-visuals-utils-testutils package. E o método createCategoricalDataViewBuilder requer a biblioteca lodash.And the createCategoricalDataViewBuilder method requires the lodash library.

Adicione esses pacotes às dependências.Add these packages to the dependencies.

Em package.json na seção devDependenciesIn package.json at devDependencies section

"lodash-es": "4.17.1",
"powerbi-visuals-utils-testutils": "2.2.0"

ChamarCall

npm install

para instalar a biblioteca lodash-es.to install lodash-es library.

Agora, você pode executar o teste de unidade novamente.Now, you can run the unit test again. Você deve obter a seguinte saída:You must get the following output:

> karma start

23 05 2017 16:19:54.318:WARN [watcher]: Pattern "E:/WORKSPACE/PowerBI/PowerBI-visuals-sampleBarChart/data/*.csv" does not match any file.
23 05 2017 16:19:58.333:WARN [karma]: No captured browser, open https://localhost:9876/
23 05 2017 16:19:58.346:INFO [karma]: Karma v1.3.0 server started at https://localhost:9876/
23 05 2017 16:19:58.346:INFO [launcher]: Launching browser Chrome with unlimited concurrency
23 05 2017 16:19:58.394:INFO [launcher]: Starting browser Chrome
23 05 2017 16:19:59.873:INFO [Chrome 58.0.3029 (Windows 10 0.0.0)]: Connected on socket /#NcNTAGH9hWfGMCuEAAAA with id 3551106
Chrome 58.0.3029 (Windows 10 0.0.0): Executed 1 of 1 SUCCESS (1.266 secs / 1.052 secs)

=============================== Coverage summary ===============================
Statements   : 56.72% ( 135/238 )
Branches     : 32.54% ( 41/126 )
Functions    : 66.67% ( 38/57 )
Lines        : 52.83% ( 112/212 )
================================================================================

Seu visual é aberto no navegador Chrome, conforme mostrado:Your visual opens in the Chrome browser, as shown:

O UT inicia no Chrome

O resumo mostra que a cobertura aumentou.The summary shows that coverage has increased. Para saber mais sobre a cobertura de código atual, abra coverage\index.html.To learn more about current code coverage, open coverage\index.html.

Índice de cobertura do UT

Ou examine o escopo da pasta src:Or look at the scope of the src folder:

Cobertura da pasta src

No escopo do arquivo, você pode exibir o código-fonte.In the scope of file, you can view the source code. Os utilitários Coverage realçarão a linha em vermelho se determinado código não for executado durante os testes de unidade.The Coverage utilities would highlight the row in red if certain code isn't executed during the unit tests.

Cobertura de código do arquivo visual.ts

Importante

A cobertura de código não significa que você tenha boa cobertura de funcionalidade do visual.Code coverage doesn't mean that you have good functionality coverage of the visual. Um teste de unidade simples fornece uma cobertura de mais de 96% em src\visual.ts.One simple unit test provides over 96 percent coverage in src\visual.ts.

Próximas etapasNext steps

Quando seu visual está pronto, você pode enviá-lo para publicação.When your visual is ready, you can submit it for publication. Para saber mais, confira Publicar visuais do Power BI no AppSource.For more information, see Publish Power BI visuals to AppSource.