Tutorial: Hinzufügen von Komponententests zu Power BI-Visualprojekten
In diesem Artikel werden die Grundlagen zur Erstellung von Komponententests für Power BI-Visualprojekte beschrieben. Dabei wird u. a. Folgendes thematisiert:
- Einrichten des Testframeworks Jasmine und des Test Runners Karma für JavaScript-Code
- Verwenden des Pakets „powerbi-visuals-utils-testutils“
- Verwenden von Pseudo- und Fakeobjekten, um Komponententests für Power BI-Visuals zu vereinfachen
Voraussetzungen
- Ein installiertes Power BI-Visualprojekt
- Eine konfigurierte Node.js-Umgebung
In den Beispielen in diesem Artikel wird das Balkendiagramm-Visual zum Testen verwendet.
Installieren und Konfigurieren des Jasmine-Testframeworks und des Test Runners Karma für JavaScript-Code
Fügen Sie der Datei package.json im Abschnitt devDependencies
die erforderlichen Bibliotheken hinzu:
"@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"
Weitere Informationen zu package.json finden Sie in der Beschreibung unter npm-package.json.
Speichern Sie die Datei package.json, und führen Sie anschließend am Speicherort der package.json-Datei den folgenden Befehl aus:
npm install
Der Paket-Manager installiert alle neuen Pakete, die package.json hinzugefügt wurden.
Konfigurieren Sie den Test Runner und die webpack
-Konfiguration, um Komponententests auszuführen.
Der folgende Code ist ein Beispiel aus der Datei 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
})
]
};
Der folgende Code ist ein Beispiel aus der Datei 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"
}
});
};
Sie können diese Konfiguration bei Bedarf ändern.
Der Code in karma.conf.js enthält die folgende Variablen:
recursivePathToTests
: der Speicherort des Testcodes.srcRecursivePath
: der Speicherort des JavaScript-Ausgabecodes nach dem Kompilieren.srcCssRecursivePath
: der Speicherort der CSS-Ausgabe nach dem Kompilieren der LESS-Datei mit Formatangaben.srcOriginalRecursivePath
: der Speicherort des Visualquellcodes.coverageFolder
: der Speicherort, an dem der Bericht zur Code Coverage erstellt werden soll.
Die Konfigurationsdatei enthält die folgenden Eigenschaften:
singleRun: true
: Tests lassen sich entweder einmalig oder auf einem CI-System (Continuous Integration) ausführen. Sie können für das Debuggen von Tests die Einstellungfalse
festlegen. Der Browser wird vom Karma-Framework weiterhin ausgeführt, damit Sie die Konsole zum Debuggen verwenden können.files: [...]
: In diesem Array können Sie die Dateien festlegen, die in den Browser geladen werden sollen. Die geladenen Dateien sind in der Regel Quelldateien, Testfälle und Bibliotheken (z. B. Jasmine oder Testhilfsprogramme). Bei Bedarf können Sie weitere Dateien hinzufügen.preprocessors
: In diesem Abschnitt konfigurieren Sie die Aktionen, die vor den Komponententests ausgeführt werden. Die Aktionen können TypeScript vorab zu JavaScript kompilieren, Quellzuordnungsdateien vorbereiten und einen Code Coverage-Bericht generieren. Sie könnencoverage
deaktivieren, wenn Sie Tests debuggen.coverage
generiert mehr Code für Code Coverage-Tests, was das Debuggen von Tests komplizierter gestaltet.
Eine Beschreibung aller Karma-Konfigurationen finden Sie auf der Seite Karma-Konfigurationsdatei.
Sie können praktischerweise unter scripts
in package.json einen Testbefehl hinzufügen:
{
"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"
}
...
}
Nun können Sie mit dem Erstellen von Komponententests beginnen.
Überprüfen des DOM-Elements des Visuals
Erstellen Sie zunächst eine Instanz des Visuals, um dieses zu testen.
Erstellen des Visualinstanz-Generators
Fügen Sie dem Ordner test die Datei visualBuilder.ts hinzu, indem Sie folgenden Code verwenden:
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");
}
}
Die build
-Methode erstellt eine Instanz des Visuals. mainElement
ist eine get-Methode, die eine Instanz des DOM-Stammelements im Visual zurückgibt. Der Getter ist optional, erleichtert jedoch das Erstellen von Komponententests.
Sie verfügen nun über eine Instanz des Visuals. Als Nächstes schreiben Sie den Testfall. Mit dem exemplarischen Testfall werden die SVG-Elemente überprüft, die beim Anzeigen des Visuals erstellt werden.
Erstellen einer TypeScript-Datei zum Schreiben von Testfällen
Fügen Sie den Testfällen die Datei visualTest.ts hinzu, indem Sie folgenden Code verwenden:
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();
});
});
});
Mehrere Jasmine-Methoden werden aufgerufen:
describe
: beschreibt einen Testfall. Im Kontext des Jasmine-Frameworks beschreibtdescribe
häufig eine Sammlung oder eine Gruppe von Spezifikationen.beforeEach
: wird vor jedem Aufruf derit
-Methode aufgerufen, die in der Methodedescribe
definiert ist.it
: definiert eine einzelne Spezifikation. Dieit
-Methode sollte eine oder mehrereexpectations
enthalten.expect
: erstellt eine Erwartung für eine Spezifikation. Eine Spezifikation ist erfolgreich, wenn alle Erwartungen ohne Fehler erfüllt werden.toBeInDOM
: eine der matchers-Methoden. Weitere Informationen zu Matchern finden Sie unter Jasmine-Namespace: matchers.
Weitere Informationen zu Jasmine finden Sie auf der Seite Dokumentation zum Jasmine-Framework.
Starten von Komponententests
Dieser Test überprüft, ob das SVG-Stammelement für das Visual vorhanden ist, wenn das Visual ausgeführt wird. Geben Sie den folgenden Befehl im Befehlszeilentool ein, um den Komponententest auszuführen:
npm run test
karma.js
führt den Testfall im Chrome-Browser aus.
Hinweis
Sie müssen Google Chrome lokal installieren.
Im Befehlszeilenfenster wird die folgende Ausgabe angezeigt:
> 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 )
================================================================================
Hinzufügen statischer Daten zu Komponententests
Erstellen Sie die Datei visualData.ts im Ordner test, indem Sie folgenden Code verwenden:
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;
}
}
Die SampleBarChartDataBuilder
-Klasse erbt von TestDataViewBuilder
und implementiert die abstrakte Methode getDataView
.
Wenn Sie Daten in Datenfeldbuckets eingeben, generiert Power BI ein dataview
-Kategorieobjekt, das auf den Daten basiert.
In Komponententests haben Sie keinen Zugriff auf Power BI-Kernfunktionen, die normalerweise zum Reproduzieren der Daten verwendet werden. Sie müssen die statischen Daten jedoch dem dataview
-Kategorieobjekt zuordnen. Verwenden Sie die TestDataViewBuilder
-Klasse, um Ihre statischen Daten zuzuordnen.
Weitere Informationen zur Zuordnung von Datenansichten finden Sie unter DataViewMappings.
In der getDataView
-Methode rufen Sie die createCategoricalDataViewBuilder
-Methode mit Ihren Daten auf.
In der capabilities.json-Datei für das sampleBarChart
-Visual gibt es die Objekte dataRoles
und 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"
}
}
]
}
}
}
],
Um die gleiche Zuordnung zu generieren, müssen Sie die folgenden Parameter auf die createCategoricalDataViewBuilder
-Methode festlegen:
([
{
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)
Dabei ist this.valuesCategory
ein Array von Kategorien:
public valuesCategory: string[] = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
this.valuesMeasure
ist ein Array von Measures für jede Kategorie:
public valuesMeasure: number[] = [742731.43, 162066.43, 283085.78, 300263.49, 376074.57, 814724.34, 570921.34];
Die endgültige Version von visualData.ts enthält den folgenden Code:
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;
}
}
Jetzt können Sie die SampleBarChartDataBuilder
-Klasse im Komponententest verwenden.
Die ValueType
-Klasse ist im Paket „powerbi-visuals-utils-testutils“ definiert.
Fügen Sie das powerbi-visuals-utils-testutils-Paket zu den Abhängigkeiten hinzu. Suchen Sie in der Datei package.json
den Abschnitt dependencies
, und fügen Sie folgenden Code hinzu:
"powerbi-visuals-utils-testutils": "^2.4.1",
Aufruf
npm install
um das Paket powerbi-visuals-utils-testutils
zu installieren.
Nun können Sie den Komponententest erneut ausführen. Folgende Ausgabe muss angezeigt werden:
> 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 )
================================================================================
Aus der Zusammenfassung geht hervor, dass sich die Code Coverage erhöht hat. Weitere Informationen zur aktuellen Code Coverage finden Sie in der Datei coverage/html-report/index.html
.
Sie können sich auch die Code Coverage für den Ordner src
ansehen:
Sie können sich für jede Datei den Quellcode anzeigen lassen. Die coverage
-Hilfsprogramme heben Zeilen rot hervor, wenn bestimmte Codezeilen bei Komponententests nicht ausgeführt werden.
Wichtig
Die Code Coverage sagt nichts darüber aus, ob ein Großteil der Visualfunktionen getestet wurde. Ein einfacher Komponententest führt zu einer Code Coverage von über 96 % in src/barChart.ts
.
Debuggen
Um Ihre Tests über die Browserkonsole zu debuggen, ändern Sie in der Datei karma.conf.ts den Wert von singleRun
in false
. Mit dieser Einstellung wird Ihr Browser weiterhin ausgeführt, wenn der Browser nach der Ausführung der Tests gestartet wird.
Das Visual wird im Chrome-Browser geöffnet.
Zugehöriger Inhalt
Sobald Ihr Visual bereit ist, können Sie es zur Veröffentlichung übermitteln. Weitere Informationen finden Sie unter Veröffentlichen von Power BI-Visuals in AppSource.