Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet article décrit les principes fondamentaux de l’écriture de tests unitaires pour vos visuels Power BI, notamment comment :
- Configurer le framework d’exécuteur de test Karma JavaScript, Jasmine.
- Utiliser le package powerbi-visuals-utils-testutils.
- Utiliser des simulacres et des substituts pour aider à simplifier les tests unitaires des visuels Power BI.
Prérequis
- Un projet de visuels Power BI installé
- Un environnement Node.js configuré
Les exemples présentés dans cet article utilisent le visuel de graphique à barres à des fins de test.
Installer et configurer l’exécuteur de test Karma JavaScript et Jasmine
Ajoutez les bibliothèques requises au fichier package.json dans la section 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"
Pour en savoir plus sur package.json, consultez la description dans npm-package.json.
Enregistrez le fichier package.json, puis exécutez la commande suivante à l’emplacement du fichier package.json :
npm install
Le gestionnaire de package installe tous les nouveaux packages qui sont ajoutés à package.json.
Pour exécuter des tests unitaires, configurez Test Runner et webpack
config.
Le code suivant est un exemple de fichier 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
})
]
};
Le code suivant est un exemple de fichier 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"
}
});
};
Si nécessaire, vous pouvez modifier cette configuration.
Le code dans karma.conf.js contient les variables suivantes :
recursivePathToTests
: localise le code de test.srcRecursivePath
: localise le code JavaScript de sortie après la compilation.srcCssRecursivePath
: localise le CSS de sortie après la compilation d’un fichier LESS avec des styles.srcOriginalRecursivePath
: localise le code source de votre visuel.coverageFolder
: détermine l’emplacement de création du rapport de couverture.
Le fichier de configuration comprend les propriétés suivantes :
singleRun: true
: les tests sont exécutés sur un système d’intégration continue (CI), ou peuvent être exécutés une seule fois. Vous pouvez remplacer la valeur du paramètre parfalse
pour déboguer vos tests. L’infrastructure Karma maintient le navigateur en cours d’exécution afin que vous puissiez utiliser la console pour le débogage.files: [...]
: dans ce tableau, vous pouvez spécifier les fichiers à charger dans le navigateur. Les fichiers que vous chargez sont généralement des fichiers sources, des cas de test et des bibliothèques (comme Jasmine ou des utilitaires de test). Vous pouvez ajouter d’autres fichiers si nécessaire.preprocessors
: dans cette section, vous configurez les actions qui s’exécutent avant l’exécution des tests unitaires. Les actions peuvent précompiler le code TypeScript en JavaScript, préparer les fichiers de mappage sources et générer un rapport de couverture du code. Vous pouvez désactivercoverage
quand vous déboguez vos tests.coverage
génère plus de code pour tester la couverture du code, ce qui complique les tests de débogage.
Pour obtenir une description de toutes les configurations Karma, accédez à la page sur le fichier de configuration Karma.
Pour plus de commodité, vous pouvez ajouter une commande de test dans les scripts
du fichier 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"
}
...
}
Vous êtes maintenant prêt à commencer l’écriture de vos tests unitaires.
Vérifier l’élément DOM du visuel
Pour tester le visuel, commencez par créer une instance de celui-ci.
Créer un générateur d’instances de visuels
Ajoutez un fichier visualBuilder.ts au dossier test à l’aide du code suivant :
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");
}
}
La build
méthode crée une instance de votre visuel. mainElement
est une méthode GET, qui retourne une instance de l’élément DOM (Document Object Model) racine dans votre visuel. L’accesseur Get est facultatif, mais facilite l’écriture des tests unitaires.
Vous disposez maintenant d’une build d’une instance de votre visuel. Écrivons le cas de test. L’exemple de cas de test vérifie les éléments SVG qui sont créés lors de l’affichage de votre visuel.
Créer un fichier TypeScript pour écrire des cas de test
Ajoutez un fichier visualTest.ts pour les cas de test à l’aide du code suivant :
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();
});
});
});
Plusieurs méthodes Jasmine sont appelées :
describe
: décrit un cas de test. Dans le contexte de l’infrastructure Jasmine,describe
décrit souvent une suite ou un groupe de spécifications.beforeEach
: est appelée avant chaque appel de la méthodeit
, qui est définie dans la méthodedescribe
.it
: définit une spécification unique. La méthodeit
doit contenir un ou plusieursexpectations
.expect
: crée une attente pour une spécification. Une spécification réussit si toutes les attentes réussissent sans aucun échec.toBeInDOM
: est l’une des méthodes matchers. Pour plus d’informations sur les matchers, consultez Jasmine Namespace: matchers.
Pour plus d’informations sur Jasmine, consultez la page de documentation du framework Jasmine.
Lancer des tests unitaires
Ce test vérifie que l’élément SVG racine de votre visuel existe quand le visuel s’exécute. Pour exécuter le test unitaire, entrez la commande suivante dans l’outil en ligne de commande :
npm run test
karma.js
exécute le cas de test dans le navigateur Chrome.
Remarque
Vous devez installer Google Chrome localement.
Dans la fenêtre de ligne de commande, vous obtenez la sortie suivante :
> 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 )
================================================================================
Comment ajouter des données statiques pour les tests unitaires
Créez le fichier visualData.ts dans le dossier test à l’aide du code suivant :
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;
}
}
La classe SampleBarChartDataBuilder
étend TestDataViewBuilder
et implémente la méthode abstraite getDataView
.
Quand vous placez des données dans des compartiments de champs de données, Power BI produit un objet dataview
catégorique basé sur vos données.
Dans des tests unitaires, vous n’avez pas accès aux fonctions principales Power BI que vous utilisez normalement pour reproduire les données. Toutefois, vous devez mapper vos données statiques au dataview
catégorique. Utilisez la classe TestDataViewBuilder
pour mapper vos données statiques.
Pour plus d’informations sur le mappage des vues de données, consultez DataViewMappings.
Dans la méthode getDataView
, vous appelez la méthode createCategoricalDataViewBuilder
avec vos données.
Dans le fichier capabilities.json du visuel sampleBarChart
, nous avons les objets dataRoles
et 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"
}
}
]
}
}
}
],
Pour générer le même mappage, vous devez définir les paramètres suivants sur la méthode 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)
Où this.valuesCategory
est un tableau de catégories :
public valuesCategory: string[] = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
Et this.valuesMeasure
est un tableau de mesures pour chaque catégorie :
public valuesMeasure: number[] = [742731.43, 162066.43, 283085.78, 300263.49, 376074.57, 814724.34, 570921.34];
La version finale de visualData.ts contient le code suivant :
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;
}
}
À présent, vous pouvez utiliser la classe SampleBarChartDataBuilder
dans votre test unitaire.
La classe ValueType
est définie dans le package powerbi-visuals-utils-testutils.
Ajoutez le package powerbi-visuals-utils-testutils aux dépendances. Dans le fichier package.json
, recherchez la section dependencies
et ajoutez le code suivant :
"powerbi-visuals-utils-testutils": "^2.4.1",
Appeler
npm install
pour installer le package powerbi-visuals-utils-testutils
.
À présent, vous pouvez réexécuter le test unitaire. Vous devez obtenir la sortie suivante :
> 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 )
================================================================================
Le résumé indique que la couverture a augmenté. Pour en savoir plus sur la couverture du code actuelle, ouvrez le fichier coverage/html-report/index.html
.
Ou examinez l’étendue du dossier src
:
Dans l’étendue du fichier, vous pouvez voir le code source. Les utilitaires coverage
mettent en évidence la ligne en rouge si certaines lignes de code ne s’exécutent pas pendant les tests unitaires.
Important
La couverture du code ne signifie pas que vous avez une bonne couverture des fonctionnalités du visuel. Un simple test unitaire fourni plus de 96 pour cent de couverture dans src/barChart.ts
.
Débogage
Pour déboguer vos tests via la console du navigateur, modifiez la valeur singleRun
dans karma.conf.ts en false
. Ce paramètre maintient votre navigateur en cours d’exécution quand il démarre après l’exécution des tests.
Votre visuel s’ouvre dans le navigateur Chrome.
Contenu connexe
Quand votre visuel est prêt, vous pouvez le soumettre pour publication. Pour plus d’informations, consultez Publier des visuels Power BI dans AppSource.