同値分割テストと境界値テストについて

## 同値分割テストとは

同値分割テストはソフトウェアテスト手法の一つです。これは、入力データを同じ結果を引き起こすと期待されるグループ (パーティション)に分割し、各パーティションから代表的な値をテストする方法です。
以下にその手法を簡単に説明します。

### 基本概念
1. **同値クラスの特定**
    - 入力データを、同じ種類の動作を引き起こす可能性が高いグループ  (**同値クラス**)に分けます。
    - 例えば、ある入力フィールドに対して、許容される範囲の数値 (有効同値クラス)と許容されない範囲の数値 (無効同値クラス)に分割します。
 　
2. **各クラスから代表値を選ぶ**
    - 各同値クラスから1つか2つの代表的な値を選び、その値をテストに使用します。代表値には、**範囲の中間値**を選択することが多いです。その他の代表値の選定方法としては、「**デフォルトの設定値**」や「**入力される頻度の高い値**」が選定されることもあります。
    - 同値クラス内のどの値を選んでも同じ結果が得られると期待されるため、全ての値をテストする必要はありません。

### 例1: 入力が1から100の整数を受け付けるフィールドの場合

- **有効同値クラス**: 1-100の数値
    - 代表値: 1, 50, 100
　
- **無効同値クラス**:
    - 100より大きい数値 (例: 101)
    - 1より小さい数値 (例: 0)
    - 数値以外 (例: "abc", -5)

　
同値分割テストの例をコードで示します。まず、テストする関数を定義します。
　
**検証関数**
```tsx
// validateNumberInput.js

function validateNumberInput(input) {
    return Number.isInteger(input) && input >= 1 && input <= 100;
}

module.exports = validateNumberInput;
```

次に、上記の関数に対する同値分割テストケースを記述します。ここでは、テストフレームワークとしてJestを使用します。
　
**テストコード**
```tsx
// validateNumberInput.test.js

const validateNumberInput = require('./validateNumberInput');

describe('validateNumberInput', () => {
    // 有効同値クラスのテスト
    test('should return true for valid numbers (1, 50, 100)', () => {
        expect(validateNumberInput(1)).toBe(true);
        expect(validateNumberInput(50)).toBe(true);
        expect(validateNumberInput(100)).toBe(true);
    });

    // 無効同値クラスのテスト
    test('should return false for numbers outside valid range (0, 101)', () => {
        expect(validateNumberInput(0)).toBe(false);
        expect(validateNumberInput(101)).toBe(false);
    });

    test('should return false for non-integer values ("abc", -5)', () => {
        expect(validateNumberInput("abc")).toBe(false);
        expect(validateNumberInput(-5)).toBe(false);
    });
});
```

### 例2: ユーザー名フィールドが3から10文字のアルファベットを受け付ける場合

- **有効同値クラス**: 3-10文字のアルファベット
    - 代表値: "abc", "abcdefghij"
　
- **無効同値クラス**:
    - 3文字未満の入力 (例: "ab")
    - 10文字より多い入力 (例: "abcdefghijk")
    - アルファベット以外の文字を含む (例: "abc123", "abc!")

　
**検証関数**
```tsx
// validateUsername.js

function validateUsername(input) {
    const regex = /^[a-zA-Z]{3,10}$/;
    return regex.test(input);
}

module.exports = validateUsername;
```

　
**テストコード**
```tsx
// validateUsername.test.js

const validateUsername = require('./validateUsername');

describe('validateUsername', () => {
    // 有効同値クラスのテスト
    test('should return true for valid usernames ("abc", "abcdefghij")', () => {
        expect(validateUsername("abc")).toBe(true);
        expect(validateUsername("abcdefghij")).toBe(true);
    });

    // 無効同値クラスのテスト
    test('should return false for usernames with less than 3 characters ("ab")', () => {
        expect(validateUsername("ab")).toBe(false);
    });

    test('should return false for usernames with more than 10 characters ("abcdefghijk")', () => {
        expect(validateUsername("abcdefghijk")).toBe(false);
    });

    test('should return false for usernames containing non-alphabet characters ("abc123", "abc!")', () => {
        expect(validateUsername("abc123")).toBe(false);
        expect(validateUsername("abc!")).toBe(false);
    });
});
```

### メリット
同値クラスを使うことで、全ての入力値を個別にテストする必要がなく、テストケースの数を大幅に減らすことができます。そのため、テストの効率化を図ることができます。
また、代表値を選ぶことで異なるクラスの動作をカバーし重要なエラーを見逃すリスクを減らせます。
同値分割テストはテストケースの設計を効率化しつつ、入力データの多様な範囲をカバーするのに役立つ有効な手法です。


## 境界値テストとは
境界値テストは、ソフトウェアテストの手法の一つで、入力の境界値に着目してテストを行う方法です。
この手法では、入力の範囲の端（境界）にある値やその近傍の値をテストすることで、エラーが発生しやすい部分を重点的にチェックします。**境界値は、ソフトウェアのバグが発生しやすい場所である**ため、このテスト手法は非常に効果的です。

### 基本概念
1. **境界値の特定** 
    入力値の範囲の上限と下限、およびその周辺の値を特定します。
　
2. **境界値とその近傍をテスト**
    境界値そのもの、境界値の直前、境界値の直後の値をテストします。

### 例1: 入力が1から100の整数を受け付けるフィールドの場合
- **境界値**: 1, 100
- **テストする値**:
    - 下限境界値: 1
    - 下限境界値の直前: 0
    - 下限境界値の直後: 2
    - 上限境界値: 100
    - 上限境界値の直前: 99
    - 上限境界値の直後: 101

**検証関数**
```tsx
// validateNumberInput.js

function validateNumberInput(input) {
    return Number.isInteger(input) && input >= 1 && input <= 100;
}

module.exports = validateNumberInput;
```

**テストコード**
```tsx
// validateNumberInput.test.js

const validateNumberInput = require('./validateNumberInput');

describe('validateNumberInput - Boundary Value Testing', () => {
    // 下限境界値とその周辺
    test('should return true for lower boundary value (1)', () => {
        expect(validateNumberInput(1)).toBe(true);
    });

    test('should return false for value just below lower boundary (0)', () => {
        expect(validateNumberInput(0)).toBe(false);
    });

    test('should return true for value just above lower boundary (2)', () => {
        expect(validateNumberInput(2)).toBe(true);
    });

    // 上限境界値とその周辺
    test('should return true for upper boundary value (100)', () => {
        expect(validateNumberInput(100)).toBe(true);
    });

    test('should return true for value just below upper boundary (99)', () => {
        expect(validateNumberInput(99)).toBe(true);
    });

    test('should return false for value just above upper boundary (101)', () => {
        expect(validateNumberInput(101)).toBe(false);
    });
});
```

### 例2: ユーザー名フィールドが3から10文字のアルファベットを受け付ける場合
- **境界値**: 3文字, 10文字
　
- **テストする値**:
    - 下限境界値: "abc"（3文字）
    - 下限境界値の直前: "ab"（2文字）
    - 下限境界値の直後: "abcd"（4文字）
    - 上限境界値: "abcdefghij"（10文字）
    - 上限境界値の直前: "abcdefghi"（9文字）
    - 上限境界値の直後: "abcdefghijk"（11文字）

　
**検証関数**

```tsx
// validateUsername.js

function validateUsername(input) {
    const regex = /^[a-zA-Z]{3,10}$/;
    return regex.test(input);
}

module.exports = validateUsername
```
　
**テストコード**
```tsx
// validateUsername.test.js

const validateUsername = require('./validateUsername');

describe('validateUsername - Boundary Value Testing', () => {
    // 下限境界値とその周辺
    test('should return true for lower boundary value ("abc")', () => {
        expect(validateUsername("abc")).toBe(true);
    });

    test('should return false for value just below lower boundary ("ab")', () => {
        expect(validateUsername("ab")).toBe(false);
    });

    test('should return true for value just above lower boundary ("abcd")', () => {
        expect(validateUsername("abcd")).toBe(true);
    });

    // 上限境界値とその周辺
    test('should return true for upper boundary value ("abcdefghij")', () => {
        expect(validateUsername("abcdefghij")).toBe(true);
    });

    test('should return true for value just below upper boundary ("abcdefghi")', () => {
        expect(validateUsername("abcdefghi")).toBe(true);
    });

    test('should return false for value just above upper boundary ("abcdefghijk")', () => {
        expect(validateUsername("abcdefghijk")).toBe(false);
    });
});
```