2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptでペアワイズ法によるテストを効率化するツール「covertable」

Last updated at Posted at 2025-07-29

要約

  • PICTは、コマンドラインツールであり、独自の文法も持っているので使いにくい
  • covertableならば、ペアワイズ法によるテストデータの生成を手軽に実現できる

想定する読者

  • ペアワイズ法の原理をなんとなくは理解している
  • ペアワイズ法をテストデータの生成に利用したい

この記事を読む利点

  • 学習コストが高いPICTよりも迅速にペアワイズ法を体感できる
  • ペアワイズ法によるテストデータ生成をmocha+chaiなどの自動テスト環境に簡便に導入できる

記事の目的

ソフトウェアテストにおいてテストデータの網羅性を高めようとすると、値の組合せ爆発という問題に直面します。この問題の解決策の1つとしては、ペアワイズ法(オールペア法)によってテストデータの個数を効率的に絞り込む方法があります。

しかし、この記事の執筆時点で、Qiitaを「ペアワイズ」などのキーワードで検索し[1]た結果、得られた記事のほとんどは以下のいずれかでした:

  • ペアワイズ法の原理の説明
  • テストデータ生成ツールPICTの使用方法の解説

このうち、PICTは、コマンドラインツールであり、制約条件を指定するための独自の文法を持っています。:

IF [File system] = "FAT" THEN [Size] <= 4096;
IF [File system] = "FAT32" THEN [Size] <= 32000;

――引用元: [2]

こうした特徴は、筆者にとっては使いにくい印象でした。また、筆者は、個人開発プロジェクトでmocha+chaiを自動テスト環境として採用しており、この環境で簡便に利用できるペアワイズ法ライブラリを探索した結果、covertableが最も使いやすいと判断しました。

ところが、「covertable」でQiitaを検索しても解説記事が見当たらず[3]、ドキュメントも英語のみ[2]で日本人開発者には参入障壁が高い状況でした。

そこで、この記事では、covertableを用いたペアワイズ法によるテストデータの効率的な生成方法を解説します。

covertableの使用手順

では、早速、covertableを使っていきましょう。

1. covertableの導入

ライブラリ名: covertable
URL: https://www.npmjs.com/package/covertable

npm install --save-dev covertable

2. make関数のインポートと呼出し

2.1. 各因子の値を2次元配列で渡す場合

コードとその実行結果は、以下の通りです。

import { make } from "covertable";

const foo = [true, false];
const bar = [2, 3, 5];
const baz = ["e", "t", "o", "i", "n"];

const qux = make([foo, bar, baz]);

console.log("テストデータ: ")
console.log(qux);
console.log("\nテストデータの個数の比較: ")
console.log(`  全網羅: ${foo.length * bar.length * baz.length}通り`);
console.log(`  ペアワイズ法: ${qux.length}通り`);
テストデータ: 
[
  [ false, 3, 'e' ], [ true, 3, 'o' ],
  [ false, 2, 't' ], [ false, 5, 'i' ],
  [ true, 5, 't' ],  [ true, 2, 'e' ],
  [ false, 2, 'o' ], [ true, 5, 'e' ],
  [ false, 5, 'n' ], [ true, 3, 'i' ],
  [ true, 5, 'o' ],  [ true, 2, 'i' ],
  [ true, 3, 'n' ],  [ true, 3, 't' ],
  [ true, 2, 'n' ]
]

テストデータの個数の比較: 
  全網羅: 30通り
  ペアワイズ法: 15通り

2.2. 各因子の値をオブジェクトで渡す場合

コードとその実行結果は、以下の通りです。

import { make } from "covertable";

const foo = [true, false];
const bar = [2, 3, 5];
const baz = ["e", "t", "o", "i", "n"];

const qux = make({ foo, bar, baz });

console.log("テストデータ: ");
console.log(qux);
console.log("\nテストデータの個数の比較: ");
console.log(`  全網羅: ${foo.length * bar.length * baz.length}通り`);
console.log(`  ペアワイズ法: ${qux.length}通り`);
テストデータ: 
[
  { foo: false, baz: 'e', bar: 3 },
  { bar: 3, baz: 'o', foo: true },
  { foo: false, baz: 't', bar: 2 },
  { bar: 5, baz: 'i', foo: false },
  { bar: 5, baz: 't', foo: true },
  { foo: true, bar: 2, baz: 'e' },
  { foo: false, baz: 'o', bar: 2 },
  { bar: 5, baz: 'e', foo: true },
  { bar: 5, baz: 'n', foo: false },
  { bar: 3, baz: 'i', foo: true },
  { bar: 5, baz: 'o', foo: true },
  { bar: 2, baz: 'i', foo: true },
  { bar: 3, baz: 'n', foo: true },
  { bar: 3, baz: 't', foo: true },
  { bar: 2, baz: 'n', foo: true }
]

テストデータの個数の比較: 
  全網羅: 30通り
  ペアワイズ法: 15通り

3. 制約条件の追加

制約条件は、make関数の第2引数に指定します。
指定の仕方は、preFilterとpostFilterがあります。
これらは、フィルタリングするタイミングのみが異なります。
それによって、結果も以下のように異なります。

3.1. preFilter

コードとその実行結果は、以下の通りです。

import { make } from "covertable";
import { SuggestRowType } from "covertable/dist/types";

const foo = [true, false];
const bar = [2, 3, 5];
const baz = ["e", "t", "a", "o", "i"];

const factors = { foo, bar, baz };

const qux = make(factors, {
  // fooが偽に等しく、bazがiに等しいケースのみ抽出する。
  preFilter: (row: SuggestRowType<typeof factors>) =>
    row.foo === false && row.baz === "i",
});

console.log(qux);
[
  { foo: false, bar: 3, baz: 'i' },
  { bar: 5, baz: 'i', foo: false },
  { bar: 2, baz: 'i', foo: false }
]
3.2. postFilter

コードとその実行結果は、以下の通りです。

import { make } from "covertable";
import { SuggestRowType } from "covertable/dist/types";

const foo = [true, false];
const bar = [2, 3, 5];
const baz = ["e", "t", "a", "o", "i"];

const factors = { foo, bar, baz };

const qux = make(factors, {
  // fooが偽に等しく、bazがiに等しいケースのみ抽出する。
  postFilter: (row: SuggestRowType<typeof factors>) =>
    row.foo === false && row.baz === "i",
});

console.log(qux);
[ { bar: 5, baz: 'i', foo: false } ]

考察:実際のユースケース

実際の使用場面を考えてみます。

ここでは、数学の定理「相加平均 >= 相乗平均」をプログラミングで表現し、それをペアワイズ法でテストする、という状況を想定しています。

コードとその実行結果は、以下の通りです。

import { make } from "covertable";

// 3数の相加平均を計算する関数
const arithmeticMean = (a: number, b: number, c: number) => {
  return (a + b + c) / 3;
};

// 3数の相乗平均を計算する関数
const geometricMean = (a: number, b: number, c: number) => {
  return Math.cbrt(a * b * c);
};

// 0から9までの整数の3つの組合せをペアワイズ法で生成する。
const numZeroToNine = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const testData = make({ a: numZeroToNine, b: numZeroToNine, c: numZeroToNine });

console.log("テストデータの個数: ");
console.log(`  全網羅: ${Math.pow(numZeroToNine.length, 3)}通り`);
console.log(`  ペアワイズ法: ${testData.length}通り`);

// テストを実施する。
console.log("\nテスト結果: ");
testData.forEach((nums: { a: number; b: number; c: number }) => {
  const { a, b, c } = nums;
  const aMean = arithmeticMean(a, b, c);
  const gMean = geometricMean(a, b, c);
  const isGreaterEqual = aMean >= gMean;
  console.log(`3数: (${a}, ${b}, ${c}), 命題: ${isGreaterEqual}`);
  console.assert(isGreaterEqual, "仮説に反例が見つかりました。");
});
テストデータの個数: 
  全網羅: 1000通り
  ペアワイズ法: 111通り

テスト結果: 
3数: (9, 1, 6), 命題: true
3数: (4, 6, 7), 命題: true
3数: (7, 7, 7), 命題: true
3数: (7, 1, 4), 命題: true
3数: (5, 4, 7), 命題: true
3数: (5, 5, 6), 命題: true
3数: (5, 8, 0), 命題: true
3数: (6, 9, 9), 命題: true
3数: (9, 2, 7), 命題: true
3数: (0, 5, 8), 命題: true
3数: (1, 5, 4), 命題: true
3数: (2, 9, 6), 命題: true
3数: (0, 3, 9), 命題: true
3数: (4, 1, 5), 命題: true
3数: (1, 6, 5), 命題: true
3数: (6, 6, 6), 命題: true
3数: (4, 5, 3), 命題: true
3数: (1, 9, 2), 命題: true
3数: (9, 0, 2), 命題: true
3数: (0, 7, 0), 命題: true
3数: (8, 0, 1), 命題: true
3数: (3, 2, 5), 命題: true
3数: (7, 5, 5), 命題: true
3数: (4, 2, 8), 命題: true
3数: (9, 4, 5), 命題: true
3数: (3, 3, 1), 命題: true
3数: (5, 0, 5), 命題: true
3数: (2, 8, 2), 命題: true
3数: (4, 7, 2), 命題: true
3数: (2, 4, 1), 命題: true
3数: (1, 1, 8), 命題: true
3数: (2, 6, 4), 命題: true
3数: (6, 0, 7), 命題: true
3数: (7, 0, 9), 命題: true
3数: (3, 0, 4), 命題: true
3数: (9, 8, 9), 命題: true
3数: (1, 4, 9), 命題: true
3数: (6, 2, 4), 命題: true
3数: (4, 3, 0), 命題: true
3数: (5, 3, 3), 命題: true
3数: (9, 6, 1), 命題: true
3数: (3, 1, 9), 命題: true
3数: (2, 0, 8), 命題: true
3数: (8, 9, 3), 命題: true
3数: (6, 7, 5), 命題: true
3数: (2, 2, 9), 命題: true
3数: (3, 8, 6), 命題: true
3数: (2, 1, 3), 命題: true
3数: (9, 3, 8), 命題: true
3数: (3, 7, 3), 命題: true
3数: (0, 8, 5), 命題: true
3数: (3, 6, 2), 命題: true
3数: (7, 9, 8), 命題: true
3数: (8, 3, 7), 命題: true
3数: (1, 7, 1), 命題: true
3数: (8, 7, 9), 命題: true
3数: (8, 4, 2), 命題: true
3数: (6, 4, 8), 命題: true
3数: (5, 6, 8), 命題: true
3数: (5, 1, 2), 命題: true
3数: (9, 9, 0), 命題: true
3数: (0, 9, 4), 命題: true
3数: (2, 5, 0), 命題: true
3数: (0, 0, 3), 命題: true
3数: (6, 5, 2), 命題: true
3数: (6, 1, 0), 命題: true
3数: (7, 6, 0), 命題: true
3数: (1, 3, 6), 命題: true
3数: (7, 2, 1), 命題: true
3数: (8, 8, 8), 命題: true
3数: (8, 2, 5), 命題: true
3数: (4, 3, 4), 命題: true
3数: (6, 8, 3), 命題: true
3数: (7, 4, 6), 命題: true
3数: (4, 8, 1), 命題: true
3数: (9, 7, 4), 命題: true
3数: (2, 9, 5), 命題: true
3数: (8, 5, 6), 命題: true
3数: (2, 7, 8), 命題: true
3数: (3, 9, 7), 命題: true
3数: (1, 8, 7), 命題: true
3数: (0, 1, 1), 命題: true
3数: (5, 8, 4), 命題: true
3数: (8, 1, 7), 命題: true
3数: (8, 6, 0), 命題: true
3数: (0, 6, 2), 命題: true
3数: (1, 0, 0), 命題: true
3数: (1, 2, 0), 命題: true
3数: (9, 5, 9), 命題: true
3数: (1, 4, 3), 命題: true
3数: (4, 6, 9), 命題: true
3数: (0, 2, 6), 命題: true
3数: (5, 9, 1), 命題: true
3数: (8, 4, 4), 命題: true
3数: (6, 5, 1), 命題: true
3数: (9, 2, 3), 命題: true
3数: (3, 4, 0), 命題: true
3数: (5, 7, 9), 命題: true
3数: (2, 5, 7), 命題: true
3数: (7, 3, 2), 命題: true
3数: (0, 4, 7), 命題: true
3数: (4, 0, 6), 命題: true
3数: (3, 5, 8), 命題: true
3数: (6, 3, 5), 命題: true
3数: (7, 6, 3), 命題: true
3数: (4, 4, 0), 命題: true
3数: (7, 8, 0), 命題: true
3数: (4, 9, 0), 命題: true
3数: (5, 2, 2), 命題: true
3数: (0, 7, 6), 命題: true
3数: (2, 3, 0), 命題: true

結論

covertableならば、ペアワイズ法によるテストデータの生成を手軽に実現できます。

参考文献

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?