Opplæring: Legge til enhetstester for Power BI-prosjekter med visualobjekter

Denne artikkelen beskriver det grunnleggende om skriving av enhetstester for Power BI-visualobjekter, inkludert hvordan du kan:

  • Konfigurere testrammeverket Jasmin for JavaScript-testkjøreren Karma.
  • Bruke pakken powerbi-visuals-utils-testutils.
  • Bruke etterligninger og fiktive data for å gjøre enhetstester for Power BI-visualobjekter enklere.

Forutsetninger

  • Et installert prosjekt med Power BI-visualobjekter
  • Et konfigurert Node.js-miljø

Installer og konfigurer JavaScript-testkjøreren Karma og Jasmine

Legg til de nødvendige bibliotekene i package.json-filen i delen devDependencies:

"@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"

Hvis du vil ha mer informasjon om package.json, kan du se beskrivelsen i npm-package.json.

Lagre package.json-filen, og kjør følgende kommando på plasseringen package.json:

npm install

Pakkebehandleren installerer alle nye pakker som er lagt til i package.json.

Du kan kjøre enhetstester ved å konfigurere testkjøreren og webpack-konfigurasjon.

Følgende kode er et eksempel fra test.webpack.config.js-filen:

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
        })
    ]
};

Følgende kode er et eksempel fra karma.config.ts-filen:

"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"
        }
    });
};

Du kan endre denne konfigurasjonen om nødvendig.

Koden i karma.conf.ts inneholder følgende variabel:

  • recursivePathToTests: Finner test koden

  • srcRecursivePath: Finner JavaScript-koden for utdata etter kompilering

  • srcCssRecursivePath: Finner CSS-utdata etter å ha kompilert less-fil med stiler

  • srcOriginalRecursivePath: Finner kildekoden for visualobjektet

  • coverageFolder: Bestemmer hvor dekningsrapporten skal opprettes

Konfigurasjonsfilen inneholder følgende egenskaper:

  • singleRun: true: Tester kjøres på et system med kontinuerlig integrasjon (CI), eller de kan kjøres én gang. Du kan endre innstillingen til false for å feilsøke testene. Karma holder nettleseren i gang, slik at du kan bruke konsollen til feilsøking.

  • files: [...]: I denne matrisen kan du angi filene som skal lastes inn i nettleseren. Det finnes vanligvis kildefiler, testtilfeller, biblioteker (Jasmine, testverktøy). Du kan legge til flere filer i listen etter behov.

  • preprocessors: I denne delen konfigurerer du handlinger som kjører før enhetstestene kjører. De forhåndskompilerer typescript til JavaScript, klargjør kildetilordningsfiler og genererer rapport om kodedekning. Du kan deaktivere coverage når du feilsøker testene. Dekning genererer tilleggskode for kontrollkode for testdekningen, som kompliserer feilsøking av tester.

Hvis du vil ha beskrivelser av alle Karma-konfigurasjoner, kan du gå til siden Konfigurasjonsfil for Karma.

Du kan når som helst legge til en testkommando i 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"
    }
    ...
}

Nå er du klar til å begynne å skrive enhetstestene.

Kontroller DOM-elementet i visualobjektet

For å teste visualobjektet oppretter du først en forekomst av visualobjektet.

Opprett en forekomstbygger for visualobjekt

Legg til en visualBuilder.ts-fil i test-mappen ved å bruke følgende kode:

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");
    }
}

Det finnes build metode for å opprette en forekomst av visualobjektet. mainElement er en hentemetode som returnerer en forekomst av rot-DOM-elementet (DOM, dokumentobjektmodell) i visualobjektet. Det er valgfritt å bruke henteren, men det gjør det enklere å skrive enhetstesten.

Du har nå en bygging av en forekomst av visualobjektet. La oss skrive testtilfellet. Testtilfellet kontrollerer SVG-elementene som opprettes når den visualobjektet vises.

Opprett en typescript-fil for å skrive testtilfeller

Legg til en visualTest.ts-fil for testtilfellene ved hjelp av følgende kode:

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();
    });
});

Flere metoder kalles:

  • describe: Beskriver et testtilfelle. I konteksten til Jasmine-rammeverket beskrives det ofte en serie eller gruppe med spesifikasjoner.

  • beforeEach: Kalles før hvert oppkall av metoden it, som er definert i metoden describe.

  • it: Definerer en enkelt spesifikasjon. it-metoden må inneholde ett eller flere expectations.

  • expect: Oppretter en forventning for en spesifikasjon. En spesifikasjon lykkes hvis alle forventninger oppfylles uten feil.

  • toBeInDOM: En av samsvarsmetodene. Hvis du vil ha mer informasjon om samsvar, se Jasmine Namespace: matchers.

Hvis du vil ha mer informasjon om Jasmine, se siden med dokumentasjon for Jasmine-rammeverket.

Starte enhetstester

Denne testen kontrollerer at det rot-SVG-elementet i visualobjektene er opprettet. Hvis du vil kjøre enhetstesten, skriver du inn følgende kommando i kommandolinjeverktøyet:

npm run test

karma.js kjører testtilfellet i nettleseren Chrome.

Karma JavaScript åpnet i Chrome

Obs!

Du må installere Google Chrome lokalt.

I kommandolinjevinduet får du følgende utdata:

> 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 )
================================================================================

Slik legger du til statiske data for enhetstester

Opprett visualBuilder.ts-filen i test-mappen ved å bruke følgende kode:

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;
    }
}

Klassen SampleBarChartDataBuilder utvider TestDataViewBuilder og implementerer den abstrakte metoden getDataView.

Når du legger inn data i datafeltsamlinger, genererer Power BI et kategorisk dataview-objekt som er basert på dataene dine.

Datafeltsamlinger

I enhetstester har du ikke Power BIs kjernefunksjoner til å gjenskape dataene. Men du må tilordne de statiske dataene til det kategoriske dataview. TestDataViewBuilder-klassen kan hjelpe deg med å tilordne dem.

Hvis du vil ha mer informasjon om tilordning av datavisninger, se DataViewMappings.

I getDataView-metoden kaller du createCategoricalDataViewBuilder-metoden med dataene dine.

I sampleBarChart-visualobjektets capabilities.json-fil har vi objektene dataRoles og 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"
                        }
                    }
                ]
            }
        }
    }
],

Hvis du vil generere samme tilordning, må du angi følgende parametere til createCategoricalDataViewBuilder-metoden:

([
    {
        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)

Der this.valuesCategory er en matrise med kategorier:

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

Og this.valuesMeasure er en matrise med mål for hver kategori:

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

Nå kan du bruke SampleBarChartDataBuilder-klassen i enhetstesten.

ValueType-klassen er definert i pakken powerbi-visuals-utils-testutils. Og createCategoricalDataViewBuilder-metoden krever biblioteket lodash.

Legg til disse pakkene i avhengighetene.

I package.json ved devDependencies-delen

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

Kall opp

npm install

for å lodash-es installere biblioteket.

Nå kan du kjøre enhetstesten på nytt. Du må få følgende utdata:

> 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 )
================================================================================

Visualobjektet åpnes i nettleseren Chrome, som vist:

UT starter i Chrome

Sammendraget viser at dekningsgraden har økt. For å finne ut mer om gjeldende kodedekning åpner du coverage\index.html.

UT-dekningsindeks

Eller se på området for mappen src:

Dekning for mappen src

I området til filen kan du vise kildekoden. Coverage-verktøyene vil utheve raden i rødt hvis spesifikk kode ikke utføres i løpet av enhetstestene.

Kodedekning for visual.ts-filen

Viktig!

Kodedekning betyr ikke at du har god funksjonsdekning for visualobjektet. En enkel enhetstest gir over 96 prosent dekning i src\visual.ts.

Neste trinn

Når visualobjektet er klart, kan du sende det til publisering. Hvis du vil ha mer informasjon, kan du se Publisere Power BI-visualobjekter i AppSource.