Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este artigo descreve as noções básicas de como escrever testes de unidade para seus visuais do Power BI, incluindo como:
- Configurar a estrutura de teste do executor de teste do Karma JavaScript, Jasmine.
- Usar o pacote powerbi-visuals-utils-testutils.
- Usar simulações e elementos fictícios para ajudar a simplificar o teste de unidade de visuais do Power BI.
Pré-requisitos
- Um projeto de visuais do Power BI instalado
- Um ambiente do Node.js configurado
Os exemplos neste artigo usam o visual de gráfico de barras para teste.
Instalar e configurar o executor de teste do Karma JavaScript e o Jasmine
Adicione as bibliotecas necessárias ao arquivo package.json na seção devDependencies
:
"@types/d3": "5.7.2",
"@types/d3-selection": "^1.0.0",
"@types/jasmine": "^3.10.2",
"@types/jasmine-jquery": "^1.5.34",
"@types/jquery": "^3.5.8",
"@types/karma": "^6.3.1",
"@types/lodash-es": "^4.17.5",
"coveralls": "^3.1.1",
"d3": "5.12.0",
"jasmine": "^3.10.0",
"jasmine-core": "^3.10.1",
"jasmine-jquery": "^2.1.1",
"jquery": "^3.6.0",
"karma": "^6.3.9",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage": "^2.0.3",
"karma-coverage-istanbul-reporter": "^3.0.3",
"karma-jasmine": "^4.0.1",
"karma-junit-reporter": "^2.0.1",
"karma-sourcemap-loader": "^0.3.8",
"karma-typescript": "^5.5.2",
"karma-typescript-preprocessor": "^0.4.0",
"karma-webpack": "^5.0.0",
"powerbi-visuals-api": "^3.8.4",
"powerbi-visuals-tools": "^3.3.2",
"powerbi-visuals-utils-dataviewutils": "^2.4.1",
"powerbi-visuals-utils-formattingutils": "^4.7.1",
"powerbi-visuals-utils-interactivityutils": "^5.7.1",
"powerbi-visuals-utils-tooltiputils": "^2.5.2",
"puppeteer": "^11.0.0",
"style-loader": "^3.3.1",
"ts-loader": "~8.2.0",
"ts-node": "^10.4.0",
"tslint": "^5.20.1",
"tslint-microsoft-contrib": "^6.2.0"
Para saber mais sobre package.json, consulte a descrição em npm-package.json.
Salve o arquivo package.json e execute o seguinte comando no local do arquivo package.json:
npm install
O gerenciador de pacotes instala todos os novos pacotes que são adicionados ao package.json.
Para executar testes de unidade, configure o executor e a configuração webpack
.
O código a seguir é um exemplo do arquivo test.webpack.config.js:
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:
"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 specify Chrome to use the 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.
O código em karma.conf.js contém as seguintes variáveis:
recursivePathToTests
: localiza o código de teste.srcRecursivePath
: localiza o código JavaScript de saída após a compilação.srcCssRecursivePath
: localiza o CSS de saída depois de compilar menos o arquivo com estilos.srcOriginalRecursivePath
: localiza o código-fonte do visual.coverageFolder
: determina o local em que o relatório de cobertura deve ser criado.
O arquivo de configuração inclui as seguintes propriedades:
singleRun: true
: Os testes são executados em um sistema de CI (integração contínua) ou podem ser executados uma vez. Você pode alterar a configuração parafalse
para depurar seus testes. A estrutura Karma mantém o navegador em execução para que você possa usar o console para depuração.files: [...]
: Nessa matriz, você pode especificar os arquivos a serem carregados no navegador. Os arquivos carregados normalmente são arquivos de origem, casos de teste e bibliotecas (como Jasmine ou utilitários de teste). Você pode adicionar mais arquivos conforme necessário.preprocessors
: Nesta seção, você configurará ações executadas antes da execução dos testes de unidade. As ações podem pré-compilar o TypeScript para JavaScript, preparar os arquivos de source map e gerar um relatório de cobertura de código. Você pode desabilitarcoverage
ao depurar seus testes.coverage
gera mais código para testes de cobertura de código, o que complica os testes de depuração.
Para obter descrições de todas as configurações do karma, vá para a página Arquivo de Configuração do Karma.
Para sua conveniência, você pode adicionar um comando de teste em scripts
em package.json:
{
"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.
Verificar o elemento DOM do visual
Para testar o Visual, primeiro crie uma instância do visual.
Criar um construtor de instância do visual
Adicione um arquivo visualBuilder.ts à pasta de test usando o seguinte código:
import { VisualBuilderBase } from "powerbi-visuals-utils-testutils";
import { BarChart as VisualClass } from "../src/barChart";
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");
}
}
O método build
cria uma instância do visual. mainElement
é um método Get, que retorna uma instância do elemento DOM (Modelo de Objeto do Documento) de uma raiz em seu visual. O getter é opcional, mas facilita a gravação do teste de unidade.
Agora você tem um build de uma instância do seu visual. Vamos escrever o caso de teste. O exemplo de caso de teste verifica os elementos SVG criados quando o visual é exibido.
Criar um arquivo TypeScript para escrever casos de teste
Adicione um arquivo visualTest.ts para os casos de teste usando o seguinte código:
import powerbi from "powerbi-visuals-api";
import { BarChartBuilder } from "./visualBuilder";
import { SampleBarChartDataBuilder } from "./visualData";
import DataView = powerbi.DataView;
describe("BarChart", () => {
let visualBuilder: BarChartBuilder;
let dataView: DataView;
let defaultDataViewBuilder: SampleBarChartDataBuilder;
beforeEach(() => {
visualBuilder = new BarChartBuilder(500, 500);
defaultDataViewBuilder = new SampleBarChartDataBuilder();
dataView = defaultDataViewBuilder.getDataView();
});
it("root DOM element is created", () => {
visualBuilder.updateRenderTimeout(dataView, () => {
expect(visualBuilder.mainElement[0]).toBeInDOM();
});
});
});
Vários métodos Jasmine são chamados:
describe
: Descreve um caso de teste. No contexto da estrutura do Jasmine,describe
geralmente descreve um conjunto ou grupo de especificações.beforeEach
: É chamado antes de cada chamada do métodoit
, que é definida no métododescribe
.it
: Define uma única especificação. O métodoit
deve conter um ou maisexpectations
.expect
: Cria uma expectativa para uma especificação. Uma especificação tem êxito se todas as expectativas são aprovadas sem falhas.toBeInDOM
: é um s métodos de correspondências. Para obter mais informações sobre correspondências, confira Namespace do Jasmine: correspondências.
Para obter mais informações sobre o Jasmine, confira a página Documentação da estrutura do Jasmine.
Iniciar testes de unidade
Esse teste verifica se o elemento SVG raiz do visual existe quando o visual é executado. Para executar o teste de unidade, digite o seguinte comando na ferramenta de linha de comando:
npm run test
karma.js
executa o caso de teste no navegador Chrome.
Observação
Você deve instalar o Google Chrome localmente.
Na janela de linha de comando, você obterá a seguinte saída:
> 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 unidade
Crie o arquivo visualData.ts na pasta test usando o seguinte código:
import powerbi from "powerbi-visuals-api";
import DataView = powerbi.DataView;
import { testDataViewBuilder } from "powerbi-visuals-utils-testutils";
import TestDataViewBuilder = testDataViewBuilder.TestDataViewBuilder;
export class SampleBarChartDataBuilder extends TestDataViewBuilder {
public static CategoryColumn: string = "category";
public static MeasureColumn: string = "measure";
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;
return dataView;
}
}
A classe SampleBarChartDataBuilder
estende TestDataViewBuilder
e implementa o método abstrato getDataView
.
Quando você coloca dados em buckets de campos de dados, o Power BI produz um objeto categórico dataview
baseado em seus dados.
Em testes de unidade, você não tem acesso a funções principais do Power BI que normalmente usa para reproduzir os dados. Mas você precisa mapear seus dados estáticos para dataview
categóricos. Use a classe TestDataViewBuilder
para mapear seus dados estáticos.
Para obter mais informações sobre mapeamento de Exibição de Dados, confira DataViewMappings.
No método getDataView
, você chama o método createCategoricalDataViewBuilder
com seus dados.
No arquivo capabilities.json do visual sampleBarChart
, temos os objetos dataRoles
e dataViewMapping
:
"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ê precisa definir os seguintes parâmetros para o método createCategoricalDataViewBuilder
:
([
{
source: {
displayName: "Category",
queryName: SampleBarChartDataBuilder.CategoryColumn,
type: ValueType.fromDescriptor({ text: true }),
roles: {
Category: true
},
},
values: this.valuesCategory
}
],
[
{
source: {
displayName: "Measure",
isMeasure: true,
queryName: SampleBarChartDataBuilder.MeasureColumn,
type: ValueType.fromDescriptor({ numeric: true }),
roles: {
Measure: true
},
},
values: this.valuesMeasure
},
], columnNames)
Em que this.valuesCategory
é uma matriz de categorias:
public valuesCategory: string[] = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
E this.valuesMeasure
é uma matriz de medidas para cada categoria:
public valuesMeasure: number[] = [742731.43, 162066.43, 283085.78, 300263.49, 376074.57, 814724.34, 570921.34];
A versão final de visualData.ts contém o seguinte código:
import powerbi from "powerbi-visuals-api";
import DataView = powerbi.DataView;
import { testDataViewBuilder } from "powerbi-visuals-utils-testutils";
import { valueType } from "powerbi-visuals-utils-typeutils";
import ValueType = valueType.ValueType;
import TestDataViewBuilder = testDataViewBuilder.TestDataViewBuilder;
export class SampleBarChartDataBuilder extends TestDataViewBuilder {
public static CategoryColumn: string = "category";
public static MeasureColumn: string = "measure";
public valuesCategory: string[] = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
];
public valuesMeasure: number[] = [
742731.43, 162066.43, 283085.78, 300263.49, 376074.57, 814724.34, 570921.34,
];
public getDataView(columnNames?: string[]): DataView {
let dataView: any = this.createCategoricalDataViewBuilder(
[
{
source: {
displayName: "Category",
queryName: SampleBarChartDataBuilder.CategoryColumn,
type: ValueType.fromDescriptor({ text: true }),
roles: {
category: true,
},
},
values: this.valuesCategory,
},
],
[
{
source: {
displayName: "Measure",
isMeasure: true,
queryName: SampleBarChartDataBuilder.MeasureColumn,
type: ValueType.fromDescriptor({ numeric: true }),
roles: {
measure: true,
},
},
values: this.valuesMeasure,
},
],
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;
return dataView;
}
}
agora você pode usar a classe SampleBarChartDataBuilder
em seu teste de unidade.
A ValueType
classe é definida no pacote powerbi-visuals-utils-testutils.
Adicione o pacote powerbi-visuals-utils-testutils às dependências. No arquivo package.json
, localize a seção dependencies
e adicione o seguinte código:
"powerbi-visuals-utils-testutils": "^2.4.1",
Chamar
npm install
para instalar o pacote powerbi-visuals-utils-testutils
.
Agora, você pode executar o teste de unidade novamente. Você deve obter a seguinte saída:
> 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 )
================================================================================
O resumo mostra que a cobertura aumentou. Para saber mais sobre a cobertura de código atual, abra o arquivo coverage/html-report/index.html
.
Ou examine o escopo da pasta src
:
No escopo do arquivo, você pode exibir o código-fonte. Os utilitários coverage
realçarão a linha em vermelho se determinadas linhas código não forem executadas durante os testes de unidade.
Importante
A cobertura de código não significa que você tenha boa cobertura de funcionalidade do visual. Um teste de unidade simples fornece uma cobertura de mais de 96% em src/barChart.ts
.
Depuração
Para depurar seus testes por meio do console do navegador, altere o valor de singleRun
em karma.config.ts para false
. Essa configuração manterá o navegador em execução quando ele for iniciado após a execução dos testes.
Seu visual é aberto no navegador Chrome.
Conteúdo relacionado
Quando seu visual está pronto, você pode enviá-lo para publicação. Para saber mais, confira Publicar visuais do Power BI no AppSource.