ソフトウェアテスト技法練習帳_境界値分析_03_実装してみた

# 概要

・ソフトウェアテストの境界値分析について勉強したので記録に残します。
  内容はCodepenで動く実物と設計書（もどき）です。

# 目的
・ブラウザのみで動作できるようにする。
　情報共有をしやすくしたいため、プログラマ以外の端末でも動作環境を確保できるようにする。
　極力docker,node.jsなどを動作環境に含めたくない。
 　（テストコードの動作環境はdocker有り）
 
・設計書を書く練習するため。ソースコードは手抜く感じにします。
・設計から実装、単体テストまで一貫して考える習慣をつけるため。

# 境界値分析とは？

```
ソフトウェア技法の種類
境界値分析とは、仕様条件の境界となる値とその隣の値に対してテストする技法のことです。境界値とは、ある範囲の最小値または最大値などの同値分割した領域の端にあたる値です。
```
shiftサイトより
https://service.shiftinc.jp/column/4792/

# 成果物

## codepen
畳数から面積を求めるプログラム

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="XJrdEGa" data-pen-title="software_test_1_03" data-user="rya234" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/rya234/pen/XJrdEGa">
  software_test_1_03</a> by RYA234 (<a href="https://codepen.io/rya234">@rya234</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>




## プロダクトコード

動作環境

名称|version|説明
---|--------|---
GoogleChrome|131.0.6778.86|ウェブブラウザ


```html
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>1_3畳の枚数から面積を求めるシステム</title>
</head>
    <h1>1_3畳の枚数から面積を求めるシステム</h1>
    <label for="tatami">畳数[畳] :</label>
    <input type="text" id="tatami" name="tatami" size="10" />
    <button id="run">変換</button>
    <div id="result">結果：</div>
<script>
    // event 系
window.onload = (event) => {
    const button = document.getElementById('run');
    const textBox= document.getElementById('tatami');
    const result = document.getElementById('result');

    // ボタンクリック時の処理
    button.addEventListener('click', event => {
        
        let isExceptionError = tatamiSingleItemCheck(parseInt(textBox.value));
        if(isExceptionError != true){
            result.innerHTML = "結果:" + isExceptionError;
            return;
        }
        // divタグに値を設定
        result.innerHTML = "結果:面積 " + checkTatami(parseInt(textBox.value)) +"【平方メートル】";
    });
}


// 単項目チェック
function tatamiSingleItemCheck(tatamiNum){
    try{
        if(!Number.isInteger(tatamiNum)){
            throw new TypeError("エラー 空欄か文字が入力されています。");
        }
        if(tatamiNum < -128 || tatamiNum >= 128){
            throw new RangeError("エラー  無効な値です。");
        }
        // ビジネスロジックに書くべき？？？？
        if(tatamiNum >= -128 && tatamiNum <= 0){
            throw new RangeError("エラー 畳数は１以上を入力してください");
        }
        return true;
    }catch (e) {
        return e.message;
    } finally {
        //必ず実行される処理
    }
}

// ビジネスロジック
function checkTatami(tatamiNum){
    value = tatamiNum * 1.65;
    return Math.ceil(value/0.01) * 0.01;
}
</script>
</body>
</html>


```


## テストコード

開発環境
名称|version|説明
---|--------|---
Javascript|-|プログラミング言語
HTML|HTML Living Standard|GUI
docker|27.0.3-1|コンテナ型仮想環境
Nodejs|22.9.0|単体テスト実行で使う
Jest|29.70|テストフレームワーク
jsdom|25.01|DOM操作のライブラリ　テストで使う
Github Codespaces|-|クラウドIDE


```js
// import {fromFile} from '@testing-library/jsdom';
import { test } from '@jest/globals';
import fs from "fs/promises";
import { JSDOM } from "jsdom";
// 実行コマンド
// docker-compose exec -it webserver bash -c  "npm test 1_03.test.js"
// 参考にしたやつ
// https://symfoware.blog.fc2.com/blog-entry-2685.html

;
describe('畳計算_単体テスト', () => {

  let html="";
  let dom = ""
  beforeEach(async() => {
    html = await fs.readFile("software_test/01Equivalence_Partitioning/1_03/1_03.html")
    dom =  new JSDOM(html,{runScripts: "dangerously"});
    dom.window.onload();
  })

  test.each([
    ["-129","エラー  無効な値です。"],
    ["-128","エラー 畳数は１以上を入力してください"],
    ["0","エラー 畳数は１以上を入力してください"],
    ["10","16.5"],
    ["127","209.55"],
    ["128","エラー  無効な値です。"],
  ])('同値分割のテスト', async(inputValue,expected) => {
        // テキスト入力欄を取得し値を設定
        const textBox = dom.window.document.getElementById('tatami');
        textBox.value = inputValue;
        // // ボタン取得とクリック
        const button = dom.window.document.getElementById('run');
        await button.click();
        // 結果の取得
        const result = dom.window.document.getElementById('result');
        expect(result.innerHTML).toContain(expected);
  });


  test.each([
    ["","エラー 空欄か文字が入力されています。"],
    ["iiijjk","エラー 空欄か文字が入力されています。"],
    ["aaaa","エラー 空欄か文字が入力されています。"],
    ["500","無効な値です。"],
    ["-500","無効な値です。"],
    ["-55","畳数は１以上を入力してください"],
  ])('異常系テスト', async(inputValue,expected) => {
        // テキスト入力欄を取得し値を設定
        const textBox = dom.window.document.getElementById('tatami');
        textBox.value = inputValue;
        // // ボタン取得とクリック
        const button = dom.window.document.getElementById('run');
        await button.click();
        // 結果の取得
        const result = dom.window.document.getElementById('result');
        expect(result.innerHTML).toContain(expected);
  });

});

```



# ドキュメント

## ユースケース記述
項目名|説明
--|---
ユースケース|畳数から面積を求める
主アクター|ユーザー
事前条件|無し
主シナリオ|・畳数を入力する　<br>・変換ボタンを押す<br>面積が表示される
成功時保証|なし
ビジネスルール|畳数から面積の変換式　<br> 畳数入力規則

## ビジネスルール
項目名|説明
--|---
ビジネスルール名|畳数　入力規則
内容|0以上の整数のみを許可<br> -129以下128以上は無効値エラー<br> 0以下-128以下は業務エラー

項目名|説明
--|---
ビジネスルール名|畳数から面積の変換式
内容|面積 = 1.65 *畳数　<br> ※小数第二位まで表示　第三位は切り上げ


## 画面項目

項目名|表示名|I/O|プロパティ(物理名)|種類|備考
-----|-----|---|--------|---|---
タイトル|単項目チェックサンプル|O|title|ラベル| _|
畳数|--|I|tatami|テキストボックス| _|
実行|変換|I|run|ボタン| clickイベント有り|
結果|結果：XXX|O|result|ラベル|clickイベント実行後に値が変化する|




## イベント処理

### ボタン　onClickイベント
　畳の計算をする

```plantuml
@startuml
title button click event
autonumber
actor user

box HTML
participant text as "textbox"
participant Button as "button"
participant output as "result"

endBOX
box javascript
 participant event
 participant value_check as "singleItemCheck"
 participant logic

 
 user --> text:input text
 user --> Button:pressButton
 activate event
 Button --> event:call event handler
 event-->value_check : Value Check
alt#Gold #LightBlue Successful case
    value_check -> event: Check is OK
    event -> logic : input textbox.value
    logic -> event : return result
    event --> output :  display[問題無し] by DOM
else #Pink Failure
    value_check -> event: Check is NG
    event-->output : display [error_message]  by DOM
end
 deactivate event
endbox
@enduml
```




ライフライン名|分類|説明
------------|----|---
textbox|HTML|id=tatamiに該当
button|HTML|id= runが該当
message|HTML|id= resultに相当
event|JS| Clickイベントハンドラー
singleItemCheck|JS| ビジネスルール　数値入力規則
logic|JS|ビジネスルール　畳数から面積の変換式

## 単体テスト仕様書

|テスト観点|確認内容|テスト条件|テスト手順|期待値|備考|
|--------|--------|---------|--------|------|----|
入力|畳数の入力欄がエラーになる場合。</br> 未入力 数値以外の値|-|畳数の入力欄に値（空欄, ”あああ”,"kk"）を入力する。</br>|以下のように表示</br>結果：空欄、数値以外が入力されています。|テストコード作成
入力|数値の入力欄に期待する値が場合。</br> 正常値の値|-|数値の入力欄に値（と）を入力する。</br>|以下のように表示</br>結果：問題なし。|テストコード作成
画面|初期表示|-|ページを表示する|仕様書どおりになっていること|目視で確認




# 課題点
現状、書くのに時間が掛かりますね。
あと反復練習すれば早くなると思うので日々継続するしかないですね。
それだけの価値は有ると自分は思います。


# ノートに書いた下書き

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/c6effafb-b0dc-4f24-6897-9a51f6dbf537.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/08dd80d6-b756-a821-8a6c-eb2d00bdebe8.png)



# 参考

github 差分
https://github.com/RYA234/routine_tool/commit/2a57cc0f550ac972e01cd746898d4086c6b64d80


テスト観点

https://qiita.com/nittannittan/items/2d8a342a8d77db53b70a

「はじめての設計をやり抜くための本　第２版」　p95 ユースケース分析

qiitaにCodepenを埋め込む
https://qiita.com/Qiita/items/edae7417214c8e957f54

「ソフトウェアテスト技法練習帳 ~知識を経験に変える40問」
p10 1.1 ペット用室温計