Zelfstudie: Eenheidstests voor Power BI-visualprojecten toevoegenTutorial: Add unit tests for Power BI visual projects

In dit artikel worden de basisbeginselen van het schrijven van tests voor uw Power BI-visuals beschreven, met onder meer:This article describes the basics of writing unit tests for your Power BI visuals, including how to:

  • Stel Jasmine, het testraamwerk voor het Karma JavaScript-testuitvoeringsprogramma, in.Set up the Karma JavaScript test runner testing framework, Jasmine.
  • Gebruik het pakket powerbi-visuals-utils-testutils.Use the powerbi-visuals-utils-testutils package.
  • Gebruik prototypen en nabootsingen om eenheidstests voor Power BI-visuals te vereenvoudigen.Use mocks and fakes to help simplify unit testing of Power BI visuals.

VereistenPrerequisites

  • Een geïnstalleerd Power BI-visualprojectAn installed Power BI visuals project
  • Een geconfigureerde Node.js-omgevingA configured Node.js environment

Het Karma JavaScript-testuitvoeringsprogramma en Jasmine installeren en configurerenInstall and configure the Karma JavaScript test runner and Jasmine

Voeg de vereiste bibliotheken toe aan het bestand package.json in de devDependencies-sectie: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"

Zie de beschrijving op npm-package.json voor meer informatie over package.json.To learn more about package.json, see the description at npm-package.json.

Sla het bestand package.json op en voer op de package.json-locatie de volgende opdracht uit:Save the package.json file and, at the package.json location, run the following command:

npm install

Met pakketbeheer worden alle nieuwe pakketten geïnstalleerd die zijn toegevoegd aan package.json.The package manager installs all new packages that are added to package.json.

Als u eenheidstests wilt uitvoeren, moet u het testuitvoeringsprogramma en de webpack-configuratie configureren.To run unit tests, configure the test runner and webpack config.

De volgende code is een voorbeeld van het bestand test.webpack.config: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
        })
    ]
};

De volgende code is een voorbeeld van het bestand 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"
        }
    });
};

Indien nodig kunt u deze configuratie wijzigen.If necessary, you can modify this configuration.

De code in karma.conf.js bevat de volgende variabele:The code in karma.conf.js contains the following variable:

  • recursivePathToTests: leidt naar de locatie van de testcoderecursivePathToTests: Locates the test code

  • srcRecursivePath: leidt naar de JavaScript-uitvoercode na het compilerensrcRecursivePath: Locates the output JavaScript code after compiling

  • srcCssRecursivePath: leidt naar de CSS-uitvoer na het compileren van Less-bestanden met stijlensrcCssRecursivePath: Locates the output CSS after compiling less file with styles

  • srcOriginalRecursivePath: bepaalt de plaats van de broncode van uw visualsrcOriginalRecursivePath: Locates the source code of your visual

  • coverageFolder: bepaalt waar het dekkingsrapport moet worden gemaaktcoverageFolder: Determines where the coverage report is to be created

Het configuratiebestand bevat de volgende eigenschappen:The configuration file includes the following properties:

  • singleRun: true: Tests worden uitgevoerd in een CI-systeem (continue integratie) of kunnen eenmalig worden uitgevoerd.singleRun: true: Tests are run on a continuous integration (CI) system, or they can be run one time. U kunt de instelling wijzigen in onwaar voor het opsporen van fouten in uw tests.You can change the setting to false for debugging your tests. In Karma blijft de browser actief, zodat u de console kunt gebruiken voor foutopsporing.Karma keeps the browser running so that you can use the console for debugging.

  • files: [...]: In deze matrix kunt u de bestanden opgeven die in de browser moeten worden geladen.files: [...]: In this array, you can specify the files to load to the browser. Gewoonlijk zijn er bronbestanden, testcases en bibliotheken (Jasmine, testhulpprogramma's).Usually, there are source files, test cases, libraries (Jasmine, test utilities). Indien nodig kunt u extra bestanden toevoegen aan de lijst.You can add additional files to the list, as necessary.

  • preprocessors: In deze sectie configureert u acties die worden uitgevoerd voordat de eenheidstests worden uitgevoerd.preprocessors: In this section, you configure actions that run before the unit tests run. Hiermee wordt het TypeScript vooraf gecompileerd naar JavaScript, worden brontoewijzingsbestanden voorbereid en wordt een codedekkingsrapport gegenereerd.They precompile the typescript to JavaScript, prepare source map files, and generate code coverage report. U kunt coverage uitschakelen wanneer u fouten opspoort in uw tests.You can disable coverage when you debug your tests. Met dekking wordt extra code gegenereerd voor het controleren van code voor de testdekking, wat foutopsporingstests complexer maakt.Coverage generates additional code for check code for the test coverage, which complicates debugging tests.

Ga naar de pagina Karma-configuratiebestand voor beschrijvingen van alle Karma-configuraties.For descriptions of all Karma configurations, go to the Karma Configuration File page.

Voor het gemak kunt u een testopdracht toevoegen aan 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"
    }
    ...
}

Nu bent u klaar om te beginnen met het schrijven van uw eenheidstests.You're now ready to begin writing your unit tests.

Het DOM-element van de visual controlerenCheck the DOM element of the visual

U moet eerst een instantie van de visual maken voordat u de visual kunt testen.To test the visual, first create an instance of visual.

Opbouwfunctie voor het maken van een visualinstantieCreate a visual instance builder

Voeg met de volgende code een bestand visualBuilder.ts toe aan de map test: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");
    }
}

Er is een build-methode voor het maken van een instantie van uw visual.There's build method for creating an instance of your visual. mainElement is een Get-methode, waarmee een instantie van het DOM-element(Document Object Model) 'root' in uw visual wordt geretourneerd.mainElement is a get method, which returns an instance of "root" document object model (DOM) element in your visual. De getter is optioneel, maar maakt het schrijven van de eenheidstest eenvoudiger.The getter is optional, but it makes writing the unit test easier.

U hebt nu een build van een instantie van uw visual.You now have a build of an instance of your visual. We gaan de testcase schrijven.Let's write the test case. Met de testcase worden de SVG-elementen gecontroleerd die worden gemaakt wanneer uw visual wordt weergegeven.The test case checks the SVG elements that are created when your visual is displayed.

Een TypeScript-bestand maken om testcases te schrijvenCreate a typescript file to write test cases

Voeg met de volgende code een bestand visualTest.ts voor de testcases toe: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();
    });
});

Er worden verschillende methoden aangeroepen:Several methods are called:

  • describe: Hiermee wordt een testcase beschreven.describe: Describes a test case. In de context van het Jasmine-framework wordt vaak een suite of groep specificaties beschreven.In the context of the Jasmine framework, it often describes a suite or group of specs.

  • beforeEach: Wordt aangeroepen vóór elke aanroep van de methode it, die is gedefinieerd in de methode describe.beforeEach: Is called before each call of the it method, which is defined in the describe method.

  • it: Hiermee wordt één specificatie gedefinieerd. De methode it moet een of meer expectations bevatten.it: Defines a single spec. The it method should contain one or more expectations.

  • expect: Hiermee wordt een verwachting van een specificatie gedefinieerd. Een specificatie slaagt als er foutloos aan alle verwachtingen wordt voldaan.expect: Creates an expectation for a spec. A spec succeeds if all expectations pass without any failures.

  • toBeInDOM: Dit is een van de matchers-methoden.toBeInDOM: One of the matchers methods. Zie Jasmine-naamruimte: matchers voor meer informatie over matchers.For more information about matchers, see Jasmine Namespace: matchers.

Zie de pagina Jasmine Framework-documentatie voor meer informatie over Jasmine.For more information about Jasmine, see the Jasmine framework documentation page.

Eenheidstests startenLaunch unit tests

Deze test controleert of het root-SVG-element van de visuals wordt gemaakt.This test checks that root SVG element of the visuals is created. U kunt de eenheidstest uitvoeren door de volgende opdracht in te voeren in het opdrachtregelprogramma:To run the unit test, enter the following command in the command-line tool:

npm run test

Met karma.js wordt de testcase uitgevoerd in de browser Chrome.karma.js runs the test case in the Chrome browser.

Karma JavaScript, geopend in Chrome

Notitie

U moet Google Chrome lokaal installeren.You must install Google Chrome locally.

In het volgende opdrachtregelvenster krijgt u de volgende uitvoer: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 )
================================================================================

Statische gegevens voor eenheidstests toevoegenHow to add static data for unit tests

Voeg met de volgende code het bestand visualData.ts toe aan de map test: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;
    }
}

Met de klasse SampleBarChartDataBuilder wordt TestDataViewBuilder uitgebreid en de abstracte methode getDataView geïmplementeerd.The SampleBarChartDataBuilder class extends TestDataViewBuilder and implements the abstract method getDataView.

Wanneer u gegevens in gegevensveldbuckets plaatst, produceert Power BI een categorisch dataview-object op basis van uw gegevens.When you put data into data-field buckets, Power BI produces a categorical dataview object that's based on your data.

Gegevensveldbuckets

In eenheidstests hebt u geen Power BI-kernfuncties om de gegevens te reproduceren.In unit tests, you don't have Power BI core functions to reproduce the data. U moet uw statische gegevens echter toewijzen aan de categorische dataview.But you need to map your static data to the categorical dataview. U kunt deze toewijzen met behulp van de klasse TestDataViewBuilder.The TestDataViewBuilder class can help you map it.

Zie DataViewMappings voor meer informatie over de toewijzing van gegevensweergaven.For more information about Data View mapping, see DataViewMappings.

In de methode getDataView roept u de methode createCategoricalDataViewBuilder aan met uw gegevens.In the getDataView method, you call the createCategoricalDataViewBuilder method with your data.

In het bestand capabilities.json van de visual sampleBarChart hebben we dataRoles- en dataViewMapping-objecten: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"
                        }
                    }
                ]
            }
        }
    }
],

Als u dezelfde toewijzing wilt genereren, moet u de volgende parameters instellen in de createCategoricalDataViewBuilder-methode: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)

Hierbij is this.valuesCategory een matrix met categorieën:Where this.valuesCategory is an array of categories:

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

En this.valuesMeasure is een matrix met metingen voor elke categorie: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];

Nu kunt u de klasse SampleBarChartDataBuilder in uw eenheidstest gebruiken.Now, you can use the SampleBarChartDataBuilder class in your unit test.

De klasse ValueType is gedefinieerd in het pakket powerbi-visuals-utils-testutils.The ValueType class is defined in the powerbi-visuals-utils-testutils package. Daarnaast is voor de methode createCategoricalDataViewBuilder de bibliotheek lodash vereist.And the createCategoricalDataViewBuilder method requires the lodash library.

Voeg deze pakketten toe aan de afhankelijkheden.Add these packages to the dependencies.

In package.json in de sectie devDependenciesIn package.json at devDependencies section

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

Roept uCall

npm install

aan om de lodash-es-bibliotheek te installeren.to install lodash-es library.

Nu kunt u de eenheidstest opnieuw uitvoeren.Now, you can run the unit test again. U moet de volgende uitvoer ophalen: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 )
================================================================================

Uw visual wordt geopend in de browser Chrome, zoals hier wordt weergegeven:Your visual opens in the Chrome browser, as shown:

UT wordt gestart in Chrome

In de samenvatting ziet u dat de dekking is verhoogd.The summary shows that coverage has increased. Open coverage\index.html voor meer informatie over de huidige codedekking.To learn more about current code coverage, open coverage\index.html.

UT-dekkingsindex

Of kijk naar het bereik van de map src:Or look at the scope of the src folder:

Dekking van src-map

In het bereik van het bestand kunt u de broncode bekijken.In the scope of file, you can view the source code. Met de Coverage-hulpprogramma's wordt de rij rood gemarkeerd als bepaalde code niet is uitgevoerd tijdens de eenheidstests.The Coverage utilities would highlight the row in red if certain code isn't executed during the unit tests.

Codedekking van het bestand visual.ts

Belangrijk

Codedekking betekent niet dat u een goede functionaliteitsdekking van de visual hebt.Code coverage doesn't mean that you have good functionality coverage of the visual. Eén eenvoudige eenheidstest zorgt voor meer dan 96 procent dekking in src\visual.ts.One simple unit test provides over 96 percent coverage in src\visual.ts.

Volgende stappenNext steps

Wanneer uw visual klaar is, kunt u deze indienen voor publicatie.When your visual is ready, you can submit it for publication. Raadpleeg Power BI-visuals in AppSource publiceren voor meer informatie.For more information, see Publish Power BI visuals to AppSource.