チュートリアル:Power BI のビジュアル プロジェクトの単体テストを追加するTutorial: Add unit tests for Power BI visual projects

この記事では、以下のことを行う方法など、Power BI のビジュアルに関する単体テストを作成するときの基本事項について説明します。This article describes the basics of writing unit tests for your Power BI visuals, including how to:

  • Karma JavaScript テスト ランナー テスト フレームワークである Jasmine を設定します。Set up the Karma JavaScript test runner testing framework, Jasmine.
  • powerbi-visuals-utils-testutils パッケージを使用します。Use the powerbi-visuals-utils-testutils package.
  • モックとフェイクを使用して Power BI のビジュアルの単体テストを簡略化します。Use mocks and fakes to help simplify unit testing of Power BI visuals.

前提条件Prerequisites

  • Power BI のビジュアル プロジェクトがインストールされているAn installed Power BI visuals project
  • Node.js 環境が構成されているA configured Node.js environment

Karma JavaScript テスト ランナーと Jasmine をインストールして構成するInstall and configure the Karma JavaScript test runner and Jasmine

必要なライブラリを package.json ファイルの devDependencies セクションに追加します。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"

package.json の詳細については、npm-package.json の説明を参照してください。To learn more about package.json, see the description at npm-package.json.

package.json ファイルを保存し、その package.json の場所で次のコマンドを実行します。Save the package.json file and, at the package.json location, run the following command:

npm install

パッケージ マネージャーによって、package.json に追加されるすべての新しいパッケージがインストールされます。The package manager installs all new packages that are added to package.json.

単体テストを実行するには、テスト ランナー webpack の構成を構成します。To run unit tests, configure the test runner and webpack config.

次のコードは、test.webpack.config.js ファイルのサンプルです。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
        })
    ]
};

次のコードは、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"
        }
    });
};

この構成は、必要に応じて変更できます。If necessary, you can modify this configuration.

karma.conf.js のコードには、次の変数が含まれています。The code in karma.conf.js contains the following variable:

  • recursivePathToTests:テスト コードの場所を示しますrecursivePathToTests: Locates the test code

  • srcRecursivePath:コンパイル後に出力される JavaScript コードの場所を示しますsrcRecursivePath: Locates the output JavaScript code after compiling

  • srcCssRecursivePath:スタイル情報が含まれる less ファイルのコンパイル後に出力される CSS の場所を示しますsrcCssRecursivePath: Locates the output CSS after compiling less file with styles

  • srcOriginalRecursivePath:ビジュアルのソース コードの場所を示しますsrcOriginalRecursivePath: Locates the source code of your visual

  • coverageFolder:カバレッジ レポートが作成される場所を決定しますcoverageFolder: Determines where the coverage report is to be created

構成ファイルには、次のプロパティが含まれます。The configuration file includes the following properties:

  • singleRun: true:テストは、継続的インテグレーション (CI) システムで実行するか、または 1 回実行することができます。singleRun: true: Tests are run on a continuous integration (CI) system, or they can be run one time. テストをデバッグする場合は、この設定を false に変更できます。You can change the setting to false for debugging your tests. Karma では、デバッグにコンソールを使用できるように、ブラウザーは実行されたままになります。Karma keeps the browser running so that you can use the console for debugging.

  • files: [...]:この配列では、ブラウザーに読み込むファイルを指定できます。files: [...]: In this array, you can specify the files to load to the browser. 通常は、ソース ファイル、テスト ケース、ライブラリ (jasmine、テスト ユーティリティ) があります。Usually, there are source files, test cases, libraries (Jasmine, test utilities). 必要に応じて、リストにファイルを追加できます。You can add additional files to the list, as necessary.

  • preprocessors:このセクションでは、単体テストを実行する前に実行するアクションを構成します。preprocessors: In this section, you configure actions that run before the unit tests run. それらでは、TypeScript が JavaScript にプリコンパイルされ、ソース マップ ファイルが準備されて、コード カバレッジ レポートが生成されます。They precompile the typescript to JavaScript, prepare source map files, and generate code coverage report. テストをデバッグするときは、coverage を無効にできます。You can disable coverage when you debug your tests. カバレッジではテスト カバレッジに対するチェック コード用に追加のコードが生成されるので、テストのデバッグが複雑になります。Coverage generates additional code for check code for the test coverage, which complicates debugging tests.

すべての Karma 構成の説明については、Karma 構成ファイルのページを参照してください。For descriptions of all Karma configurations, go to the Karma Configuration File page.

便宜のため、テスト コマンドを 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"
    }
    ...
}

これで、単体テストの作成を始める準備ができました。You're now ready to begin writing your unit tests.

ビジュアルの DOM 要素を確認するCheck the DOM element of the visual

ビジュアルをテストするには、最初にビジュアルのインスタンスを作成します。To test the visual, first create an instance of visual.

ビジュアル インスタンス ビルダーを作成するCreate a visual instance builder

次のコードを使用して、visualBuilder.ts ファイルを 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");
    }
}

build メソッドは、ビジュアルのインスタンスを作成するためのものです。There's build method for creating an instance of your visual. mainElement は get メソッドで、ビジュアル内の "ルート" ドキュメント オブジェクト モデル (DOM) 要素のインスタンスを返します。mainElement is a get method, which returns an instance of "root" document object model (DOM) element in your visual. このゲッターは省略可能ですが、単体テストの作成が容易になります。The getter is optional, but it makes writing the unit test easier.

これで、ビジュアルのインスタンスのビルドが完成です。You now have a build of an instance of your visual. テスト ケースを作成してみましょう。Let's write the test case. テスト ケースでは、ビジュアルが表示されるときに作成される SVG 要素がチェックされます。The test case checks the SVG elements that are created when your visual is displayed.

TypeScript ファイルを作成してテスト ケースを記述するCreate a typescript file to write test cases

次のコードを使用して、テスト ケースの visualTest.ts ファイルを追加します。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();
    });
});

複数のメソッドが呼び出されます。Several methods are called:

  • describe: テスト ケースが記述されます。describe: Describes a test case. Jasmine フレームワークのコンテキストでは、多くの場合、スペックのスイートまたはグループが記述されます。In the context of the Jasmine framework, it often describes a suite or group of specs.

  • beforeEach:describe メソッドで定義されている it メソッドの各呼び出しの前に呼び出されます。beforeEach: Is called before each call of the it method, which is defined in the describe method.

  • it: 1 つのスペックが定義されます。it メソッドには、expectations が 1 つ以上含まれている必要があります。it: Defines a single spec. The it method should contain one or more expectations.

  • expect: スペックの要求項目が作成されます。要求項目のすべてに合格すると (不合格の項目が 1 つもなければ) スペックが成功となります。expect: Creates an expectation for a spec. A spec succeeds if all expectations pass without any failures.

  • toBeInDOM:"matchers" メソッドの 1 つです。toBeInDOM: One of the matchers methods. マッチャーの詳細については、Jasmine の名前空間 matchers に関するページを参照してください。For more information about matchers, see Jasmine Namespace: matchers.

Jasmine の詳細については、Jasmine framework のドキュメントのページを参照してください。For more information about Jasmine, see the Jasmine framework documentation page.

単体テストを開始するLaunch unit tests

このテストでは、ビジュアルのルートの SVG 要素が作成できているかどうかを検査します。This test checks that root SVG element of the visuals is created. 単体テストを実行するには、コマンドライン ツールで次のコマンドを入力します。To run the unit test, enter the following command in the command-line tool:

npm run test

karma.js では、Chrome ブラウザーでテスト ケースが実行されます。karma.js runs the test case in the Chrome browser.

Chrome で開かれた Karma JavaScript

注意

Google Chrome をローカル環境にインストールする必要があります。You must install Google Chrome locally.

コマンドライン ウィンドウに、次の出力が表示されます。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 )
================================================================================

単体テスト用の静的データを追加する方法How to add static data for unit tests

次のコードを使用して、visualData.ts ファイルを 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;
    }
}

SampleBarChartDataBuilder クラスでは TestDataViewBuilder が拡張され、抽象メソッド getDataView が実装されます。The SampleBarChartDataBuilder class extends TestDataViewBuilder and implements the abstract method getDataView.

データをデータ フィールド バケットに入れると、Power BI により、そのデータに基づくカテゴリの dataview オブジェクトが生成されます。When you put data into data-field buckets, Power BI produces a categorical dataview object that's based on your data.

データ フィールド バケット

単体テストでは、データを再生成するための Power BI コア機能はありません。In unit tests, you don't have Power BI core functions to reproduce the data. ただし、静的データをカテゴリの dataview にマップする必要があります。But you need to map your static data to the categorical dataview. TestDataViewBuilder クラスがそれをマップするのに役立ちます。The TestDataViewBuilder class can help you map it.

データ ビュー マッピングの詳細については、「DataViewMappings」を参照してください。For more information about Data View mapping, see DataViewMappings.

getDataView メソッドでは、データを指定して createCategoricalDataViewBuilder メソッドを呼び出します。In the getDataView method, you call the createCategoricalDataViewBuilder method with your data.

sampleBarChart ビジュアルの capabilities.json ファイルには、dataRoles オブジェクトと dataViewMapping オブジェクトがあります。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"
                        }
                    }
                ]
            }
        }
    }
],

同じマッピングを生成するには、createCategoricalDataViewBuilder メソッドに次のパラメーターを設定する必要があります。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)

this.valuesCategory は、カテゴリの配列です。Where this.valuesCategory is an array of categories:

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

this.valuesMeasure は、各カテゴリのメジャーの配列です。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];

これで、単体テストで SampleBarChartDataBuilder クラスを使用できるようになりました。Now, you can use the SampleBarChartDataBuilder class in your unit test.

ValueType クラスは、powerbi-visuals-utils-testutils パッケージで定義されています。The ValueType class is defined in the powerbi-visuals-utils-testutils package. また、createCategoricalDataViewBuilder メソッドには lodash ライブラリが必要です。And the createCategoricalDataViewBuilder method requires the lodash library.

これらのパッケージを依存関係に追加します。Add these packages to the dependencies.

場所は package.jsondevDependencies セクションです。In package.json at devDependencies section

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

次を呼び出します。Call

npm install

これで、lodash-es ライブラリがインストールされます。to install lodash-es library.

これで、単体テストをもう一度実行できるようになりました。Now, you can run the unit test again. 次の出力を取得する必要があります。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 )
================================================================================

次のように、Chrome ブラウザーでビジュアルが開きます。Your visual opens in the Chrome browser, as shown:

Chrome で UT が起動したところ

概要では、カバレッジが増加したことが示されます。The summary shows that coverage has increased. 現在のコード カバレッジに関する詳細を確認するには、coverage\index.html を開きます。To learn more about current code coverage, open coverage\index.html.

UT のカバレッジ インデックス

または、src フォルダーのスコープを確認します。Or look at the scope of the src folder:

src フォルダーのカバレッジ

ファイルのスコープでは、ソース コードを確認できます。In the scope of file, you can view the source code. 単体テストの間に特定のコードが実行されない場合、Coverage ユーティリティではその行が赤で強調表示されます。The Coverage utilities would highlight the row in red if certain code isn't executed during the unit tests.

visual.ts ファイルのコード カバレッジ

重要

コード カバレッジはビジュアルの機能のカバレッジが十分であることを意味するものではありません。Code coverage doesn't mean that you have good functionality coverage of the visual. 1 つの簡単な単体テストで、src\visual.ts の 96% 以上のカバレッジが提供されています。One simple unit test provides over 96 percent coverage in src\visual.ts.

次の手順Next steps

ビジュアルの準備ができたら、発行用に送信できます。When your visual is ready, you can submit it for publication. 詳細については、「Power BI ビジュアルを AppSource に発行する」をご覧ください。For more information, see Publish Power BI visuals to AppSource.