ChatGPTをSWテスティングでどう活用しますか。



最近、ChatGPTがソフトウェア開発に多く影響を与えており、ソフトウェア・テスト分野も例外ではありません。ChatGPTがソフトウェア・テストでどのように適用されるのかについてこれから話してみようと思います。

ChatGPTが影響をあたえられるテスト活動は以下のようです。

* テスト・アイデアの洗い出し
* 要求仕様のレビュー
* テスト・ガイド
* コーディング（テスト・スクリプト）
* 受け入れテスト（例：Cucumber)
* テスト・データ生成
* 探索的テストのサポート



## テスト・アイデアの洗い出し

あるシステムを初めて見たとき、どのように、何をテストすべきなのか漠然としたことが多いです。この場合、ChatGPTのサポートをうけて方向を決めることができます。

例えば学校のHPをテストする際、どのようにテストをすればいいか問い合わせてみました。


![image-20230517213844316.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55902/2960ca4e-216a-1ff7-e2b7-668f09b6a5b2.png)



内容の確認、UX/UI、モバイル対応、パフォーマンス、セキュリティのテストを提案しています。



## 要求仕様のレビュー

レビューの意味は、既存の要件に不足や誤りがないかどうかを検証する活動です。すでに作成した要件があると想定して、これをレビューを受けて足りないものはないか問い合わせてみましょう。 

電子商取引(楽天など)の要件を下記のように洗い出したとしましょう。

* サービス利用のために会員登録及びログインする。
* 欲しい商品を素早く選ぶために、ホーム画面でユーザーに合った商品を推薦してくれる。
* 目的の商品を素早く見つけるためにカテゴリーを確認し、目的のカテゴリーを選択する。目的の商品を素早く見つけるために商品キーワードを検索する。
* 商品の評価を確認する。
* 出品者に気になる点を問い合わせる。
* 気に入った商品をカートに入れる。
* 購入するボタンをクリックする。
* 注文状況を確認し、配送照会をする。



なんか漏れてるのはないか問い合わせてみましょう。

![image-20230517220752510.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55902/31664c75-4d7b-c47b-e631-d1e2f8dcaeed.png)



自分なりに要求仕様の補強をするみたいです。



## テスティング・ガイド

テスティング・ガイドっては色んな意味があるんですが、普通以下のリストを含めています。

1. テストの範囲
2. テスト方法論
3. テスト・ケースの設計
4. テスト環境
5. テスト実行
6. テスト・レポート
7. テスト遂行祭の役割と責任



この中で「3.テスト・ケースの設計」部分を問い合わせてみましょう。

「設計」は状況別のテスト手法にとらわれる時があるんですが、ここで「設計」の意味は「制作」の意味です。我々の目的はケースをうまく作ることであり、最適は手法を適用するのではないです。

下記のコードを持ってテスト・ケースを作ってもらおうと思います。

```java
package com.example.sjkim;

import java.io.FileReader;
import java.io.File;
import java.io.InputStream;


import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.io.Resource;


import org.springframework.core.io.ClassPathResource;
@SpringBootApplication
public class SjkimApplication {
	public static void main(String[] args){
		SpringApplication.run(SjkimApplication.class, args);
	}
	public Integer ApplicationTest(String id, String pw) {
		JSONParser parser = new JSONParser();
		try{
			//ユーザの情報を読む
			Resource resource = new ClassPathResource("User.txt");
			//InputStream input = resource.getInputStream();
			File file = resource.getFile();
			FileReader reader = new FileReader(file);
			JSONObject jsonObject = (JSONObject) parser.parse(reader);
			reader.close();
			////////////////////
			boolean isIdExist = jsonObject.containsKey(id);
			JSONObject userObj = (JSONObject)jsonObject.get(id);
			//id, pw未入力
			if (id.length() == 0){
				return globalVar.FIRST_CASE;
			}
			if (pw.length() ==0){
				return globalVar.FOURTH_CASE;
			}
			if (isIdExist)
			{
				//HAPPY CASE
				if (pw.equals(userObj.get("pw"))){
					return globalVar.HAPPY_CASE;
				}
				else{
					//PW不一致
					return globalVar.SECOND_CASE;
				}
			}
			else{
				//IDが存在しない。
				return globalVar.THIRD_CASE;
			}			
		}catch(Exception e ){
			e.printStackTrace();
			return globalVar.UNKNOWN_CASE;
		}
	}
}
```


![image-20230517224803711.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55902/00becc9f-533e-15a8-aa79-949394403cf8.png)



```java
package com.example.sjkim;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.File;
import java.io.FileReader;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.Resource;

public class SjkimApplicationTests {

	@Test
	public void testApplicationTest() throws Exception {
		String id = "testId";
		String pw = "testPw";
		String wrongPw = "wrongPw";
		String wrongId = "wrongId";
		
		JSONObject userObj = new JSONObject();
		userObj.put(id, pw);
		
		JSONParser parser = mock(JSONParser.class);
		FileReader reader = mock(FileReader.class);
		File file = mock(File.class);
		Resource resource = mock(Resource.class);
		
		when(resource.getFile()).thenReturn(file);
		when(parser.parse(reader)).thenReturn(userObj);
		
		SjkimApplication sjkimApplication = new SjkimApplication(parser, resource);
		
		assertEquals(globalVar.FIRST_CASE, sjkimApplication.ApplicationTest("", pw));
		assertEquals(globalVar.FOURTH_CASE, sjkimApplication.ApplicationTest(id, ""));
		assertEquals(globalVar.HAPPY_CASE, sjkimApplication.ApplicationTest(id, pw));
		assertEquals(globalVar.SECOND_CASE, sjkimApplication.ApplicationTest(id, wrongPw));
		assertEquals(globalVar.THIRD_CASE, sjkimApplication.ApplicationTest(wrongId, pw));
		assertEquals(globalVar.UNKNOWN_CASE, sjkimApplication.ApplicationTest(null, null));
	}
}
```

![image-20230517230954299.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55902/054ed096-3d52-1fca-1652-06597b829f5f.png)



このように構造基盤べイシスでテスト・ケースを洗いだすのはChatGPTはとても役に立ちます。コードという正確な情報があるからです。



明細基盤ペイシスでテスト・ケースを洗いだすのは結果の検討が必須です。


![image-20230517234921197.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55902/24352813-5aac-d67d-83ce-275a5e31fa50.png)



さまざまなケースが生成されましたが、バウンダリー・ケースとか特別ケース（例：ID, PWでスペースが含まれる場合とか）の件が漏れてますので補強が必要です。

​	

## コーディング＋受け入れテスト

テスティング・スクリプトも作ってくれます。例えばcucumber+behaveで頼んでみましょう。

![image-20230518091905117.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55902/1804cb5b-b076-4eff-1775-fa74b712be0b.png)
![image-20230518091940559.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55902/036c6b23-a4c4-3db9-965f-e68509d2476c.png)



Gherkin言語でちゃんと作ってくれます。ちなみにテスト・コードまで頼んでみます。

![image-20230518100143283.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55902/1fb7e60d-0568-b78a-b2de-1560fd1b72ba.png)



```python
from behave import given, when, then
from my_app import MyApp # 実際のアプリケーションのクラス名に置き換えてください

@given('a user identified as "{gender}"')
def step_given_user_identified_as(context, gender):
    context.user = MyApp.create_user(gender=gender) # ユーザーを作成するメソッドに置き換えてください

@when('the user logs in')
def step_when_user_logs_in(context):
    context.message = MyApp.login(context.user) # ユーザーがログインしてメッセージを取得するメソッドに置き換えてください

@then('the welcome message is "{message}"')
def step_then_welcome_message_is(context, message):
    assert context.message == message, f"Expected '{message}', but got '{context.message}'"

```



## テスト・結果分析

テストの結果をChatGPTに渡すのはちょっと危なさそうです。テスト結果の中には敏感は情報がある可能性があるからです。でも分析手法については助言をもらえれます。例えば下はロードテスト結果分析の助言です。

![image-20230518103838307.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55902/0819c48c-6ded-7b9d-6126-7d0e909bbb09.png)





## テスト・データーの生成

テスト・データーは既に「テスティング・ガイド：明細基盤」でデータを生成して示したのでそれを参考にして下さい。



## 探索的テストの支援

探索的なテストをする人が周りにほとんどいないので、これを実験するのは簡単ではなかったので、ChatGPTに逆質問をしました。

![image-20230518131502965.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55902/a4ac9704-6b05-1f73-ba71-7500268d54aa.png)



4番がちょっとおかしいです。なんで自動化テストケースが探索的テストで出るのか？その代わりに「テスト・データーの生成」の方がもっと役に立たないかな…と思います。



## 結論

100%ではないが、多くの助けをテスト進行時にも得ることができます。 100%得ることが難しい部分はテスト結果分析、探索的テストなどであり、この分野では部分的に助けを得ることができるようです。

GPTがテスターを代替できるのかという問題は、おそらくGPTが開発者を代替できるのかという問題と似ているようです。細かい事項は助けを受けられるかもしれないが、これに対する状況に合わせた方向性の判断はまだ人が必要そうです。