【閲覧必須⁉】Azure OpenAIに単体テストのテストコードを生成させたい。
2023-06-23
azblob://2023/06/23/eyecatch/2023-06-23-generate-unit-test-automatically-000.jpg

皆さん、単体テストしてますか?

どうも、もしかしたら情報処理安全確保支援士になるかもしれない新卒の須山颯己です。

今回は長文の可能性があるので前置きは無しでチーム開発で学んだことを共有させていただきます。

単体テストのテストコードを生成できるプロンプトの公開と気を付けるべきことについてです。

単体テストとは

単体テストとは、

プログラムがモジュール単位で正常に動作するかを確かめるテスト工程。ソースコードの作成者がプログラムの内部設計書に基づいてホワイトボックステストを実施し、要求性能を満たしているかを確認する。

と一般的には言われています。

開発を行う上で「コードを書く」は避けては通れません。

Pythonimport random

# これは関数
def generate_random_noun():
    nouns = ['猫', '犬', '馬', 'ゾウ', 'キリン']
    return random.choice(nouns)

# これも関数
def generate_random_verb():
    verbs = ['走る', '飛ぶ', '泳ぐ', '踊る', '寝る']
    return random.choice(verbs)

# これは実行部分
random_noun = generate_random_noun()
random_verb = generate_random_verb()
funny_sentence = f"見て!あの{random_noun}{random_verb}!"
print(funny_sentence)

コードというものは、いくつかの「関数」とその関数を呼び出したりどこかに出力したりする「実行部分」の2つが主にあります。(この2つ以外にもある気がするけど今回は割愛)

単体テストとは、1つの「関数」のみで正常に動くかどうかを確かめるテストのことを言います。

ポイントは、この「のみ」がかなり大事です。

「関数」の中には

Pythonimport random

# これは関数
def generate_random_food():
    foods = ['ピザ', '寿司', 'ハンバーガー', 'カレー', 'パスタ']
    return random.choice(foods)

# これも関数
def generate_random_drink():
    drinks = ['コーラ', 'オレンジジュース', 'ビール', 'ワイン', 'ウイスキー']
    return random.choice(drinks)

# これは他の関数を呼んでるタイプの関数
def create_funny_pair():
    random_food = generate_random_food()
    random_drink = generate_random_drink()
    funny_pair = f"面白い組み合わせ:{random_food}{random_drink}!"
    return funny_pair

# これは実行部分
print(create_funny_pair())

このような1つの関数で別の関数を呼び出しているものもあると思います。単体テストを行う際は、たとえ他から関数を呼び出していたとしても文字通り「単体」でテストを行わなければなりません。(上記でいうとcreate_funny_pairの話)

単体テストのメリットは以下の通りです。
1. 早期にバグを検出:
単体テストでは、個々の関数やクラスを独立してテストすることで、開発の初期段階から潜在的な問題を見つけることができます。これにより、バグの修正コストが低くなり、システム全体の品質が向上します。また、バグが検出された場合に、問題のある箇所を特定しやすくなります。
 
2. 保守性向上:
単体テストが充実していることで、コードの変更やリファクタリングが容易になります。テストがあることで、新たな変更が既存の機能に悪影響を与えていないかを確認できます。これにより、安心してコードを改善し、長期的な保守性を確保できます。
 
3. 設計の改善:
単体テストを書くことで、コードのモジュール性や再利用性を意識するようになります。テストしやすいコードは、疎結合で、単一責任原則に従っており、依存関係が明確になっていることが一般的です。これらの特性は、良い設計につながり、コードの可読性や保守性が向上します。
 
4. ドキュメントとしての役割:
テストコードは、関数やクラスの使い方を示すドキュメントとしても機能します。テストコードを読むことで、他の開発者がそのコードの目的や使い方を理解しやすくなります。また、テストコードは実行可能なドキュメントであるため、古くなりにくく、コードの変更に追従しやすいという利点があります。
 
 

結論

細かいことは後述するので一旦、結論からどうぞ!!

※このプロンプトは完璧で究極のアイドルではないのでご了承ください。

python編

Python
# ここに単体テストを行いたい関数をコピペする

この関数の単体テストを作成してください。
以下が条件です。
- 使用言語:python
- テストフレームワーク:unittest
- 関数の引数:

- 関数の戻り値:

- 関数の説明:


main.pyから単体テストしたい関数をimportしてきてください。
分からない内容があっても、推測をしてテストコードを作成してください。
単体テストを実行したときに失敗したらエラーメッセージを出力するようにしてください。

C#編

C## ここに単体テストを行いたい関数をコピペする

この関数の単体テストを作成してください。
以下が条件です。
- 使用言語:C#
- テストフレームワーク:NUnit
- 関数の引数:

- 関数の戻り値:

- 関数の説明:


分からない内容があっても、推測をしてテストコードを作成してください。
単体テストを実行したときに失敗したらエラーメッセージを出力するようにしてください。

JavaScript編

Javascript# ここに単体テストを行いたい関数をコピペする

この関数の単体テストを作成してください。
以下が条件です。
- 使用言語:JavaScript
- テストフレームワーク:mocha
- 関数の引数:

- 関数の戻り値:

- 関数の説明:



MyMath.jsから単体テストを行いたい関数をimportしてきてください。
requireは使用しないでください
分からない内容があっても、推測をしてテストコードを作成してください。
単体テストを実行したときに失敗したらエラーメッセージを出力するようにしてください。

意識すること・気を付けること  共通編

①最低限書かなければならないこと

  • 単体テストを行いたい関数

    それはそう。

  • 「この関数の単体テストを作成してください。以下が条件です。」

    これ書かなきゃ話にならない。

  • 使用言語

    割とマジで大事。検証しましたが関数の説明と同じくらいの情報の質があります。

  • テストフレームワーク

    大事さは後述の3編で解説します。

  • 関数の引数・関数の戻り値

    意外と大事。関数の内容が難しくなればなるほど大事。

  • 関数の説明

    一言でもいいけど、関数の内容が難しければその分書いた方が良いと思う。

  • 分からない内容があっても、推測をしてテストコードを作成してください。

    あると便利。

  • 単体テストを実行したときに失敗したらエラーメッセージを出力するようにしてください。

    あると単体テストの時に便利。

②importできるかできないか問題

言語によって単体テストしたい関数とテストコードを1つのコードに共存させてもできるようですが、単体テストしたい関数とテストコードは別のファイルの方がいいと思います。理由は簡単に言うと以下のようです。

  1. 開発の妨げにならない(消し忘れて実行できなくなった!!みたいなことがなくなる)
  2. テストコードの編集が楽
  3. 体感、「別のファイルにしてください」とプロンプトで指定した方が精度がいい気がする

とりあえず、単体テストしたい関数とテストコードは別のファイルにすることを覚えておいてください。

③「これがあると詳細になるかも」な文章

プロンプトに書いたらテストコードがよりよくなるかもしれない文章をどうぞ

「この関数は( モック/スタブ )です。」

「関数の依存関係」

「テストケース: 」

→テスト対象に対して想定される入力値と、それに対応する期待される出力値をいくつかリストアップする

「テストの目的: 」

→どのようなケースやシナリオをテストしたいのか

あくまで参考なので追加はご自由にどうぞ

意識すること・気を付けること  Python編

①テストフレームワークについて

1. unittest: 

Pythonの標準ライブラリに含まれるテストフレームワークで、xUnitスタイルのテストをサポートしています。テストケースをクラスとして定義し、各テストメソッドをその中に記述します。また、setUp()やtearDown()などのメソッドを使用して、テストの前後処理を行うことができます。

2. pytest: 

Pythonの人気のあるサードパーティ製テストフレームワークで、シンプルなテストコードの記述や、多彩なプラグインによる拡張機能が特徴です。関数単位でテストケースを定義し、assert文を使って結果を確認します。また、fixtureを使って、テストデータやリソースの前後処理を行うことができます。

3. nose2: 

unittestの拡張版として開発されたテストフレームワークで、プラグインによる拡張性が高く、テストの自動発見やランダム実行などの機能があります。unittestと似た記述スタイルでテストを行いますが、プラグインを使って柔軟なカスタマイズが可能です。

4. doctest: 

Pythonの標準ライブラリに含まれるテストフレームワークで、ドキュメント内にテストケースを記述することができます。関数やクラスのdocstring内に、実行例としての入力と期待される出力を記述し、doctestがそれをテストとして実行します。ドキュメントとテストを一緒に記述できるため、小規模なプロジェクトやライブラリの簡単なテストに適しています。

②importについて

「main.pyから単体テストしたい関数をimportしてきてください。」

ここでimportの設定をしてます。「main.py」が単体テストしたい関数が入っている関数です。適宜変更してください。

6月23日時点ではunittestにおいて自動生成ができていることを確認済み

Azure OpenAIが自動生成できるレベルの関数なら精度95%!!

意識すること・気を付けること  C#編

①テストフレームワークについて

C#にはいくつかの主要なテストフレームワークがありますがその中でもよく使われるものをいくつか挙げます。

1. MSTest: 

Microsoftが開発した公式のテストフレームワークで、Visual Studioとの統合が強力な点が特徴です。MSTestを使用すると、テストプロジェクトを簡単に作成し、テストの実行や結果の表示がVisual Studio内で可能です。また、属性(Attribute)を利用してテストメソッドやテストクラスを定義します。

2. NUnit: 

C#および.NET向けのオープンソースのテストフレームワークで、xUnitスタイルのテストをサポートしています。NUnitは、MSTestと同様に属性を利用してテストメソッドやテストクラスを定義しますが、より柔軟な機能やアサーションが利用できます。また、Visual Studioの拡張機能を利用して、NUnitテストの実行や結果表示が可能です。

3. xUnit.net: 

C#および.NET向けのもうひとつのオープンソースのテストフレームワークで、NUnitの開発者が関与しています。xUnit.netは、よりモダンなアプローチでテストを記述することができ、特に.NET CoreやASP.NET Coreプロジェクトでよく使われています。Visual Studioの拡張機能や.NET CLIを利用して、xUnit.netのテストを実行できます。

②importについて

もうお気付きだと思いますが、Python・JavaScriptではあったimportの指定がありません。

じゃあ完璧で究極にいらないのかと言われると、正直分かりません。ここに関しては単体テストの実装方法の違いだと思います。僕が調べた単体テストの実装方法はimportの指定を書かなくても大丈夫でした。とりあえずは、、、

6月23日時点ではNUnit、Xunitにおいて自動生成ができていることを確認済み

ただimportや単体テストの実装方法でデバッグの内容に変化があります。

テスト内容はAzure OpenAIが自動生成できるレベルの関数なら問題なし!

意識すること・気を付けること  JavaScript編

①テストフレームワークについて

JavaScriptにはいくつかの主要なテストフレームワークがありますがその中でもよく使われるものをいくつか挙げます。

1. Jest: 

Facebookが開発したオープンソースのJavaScriptテストフレームワークで、ReactなどのフロントエンドライブラリやNode.jsのバックエンドアプリケーションで広く使われています。シンプルな設定、強力なモック機能、高速な実行速度が特徴です。

2. Mocha: 

人気のあるオープンソースのJavaScriptテストフレームワークで、ブラウザやNode.jsで実行できます。Mochaは、テストケースを記述するためのシンプルなAPIを提供し、さまざまなアサーションライブラリ(Chaiなど)やモックライブラリ(Sinonなど)と組み合わせて使用することができます。

3. Jasmine: 

オープンソースのJavaScriptテストフレームワークで、ブラウザやNode.jsで実行できます。BDD(Behavior-Driven Development)スタイルのテストをサポートしており、アサーションやモック機能が組み込まれているため、追加のライブラリなしでテストを記述できます。

4. QUnit: 

jQueryプロジェクトで使用されているオープンソースのJavaScriptテストフレームワークで、ブラウザやNode.jsで実行できます。シンプルなAPIと軽量な実装が特徴で、主に単体テストに適しています。

5. AVA: 

並列実行に焦点を当てたオープンソースのJavaScriptテストフレームワークで、Node.js環境でのみ利用できます。テストケースを並列で実行することで高速なテストが可能であり、シンプルなAPIとアサーション機能が提供されています。

②importについて

「MyMath.jsから単体テストを行いたい関数をimportしてきてください。」

「requireは使用しないでください。」

ここでimportの設定をしてます。「main.js」が単体テストしたい関数が入っている関数です。適宜変更してください。

問題の2行目ですが、requireを使うと私の実装方法では実行出来ませんでした。なので書いています。

まあ、これでも使うんですけどね(笑)

③デバッグ方法

実装する際に詰まったデバッグを紹介します。

  1. npm install mochaのインストール
  2. npm install chaiのインストール
  3. 「const assert = require('assert');」→「import assert from 'assert'」
  4. 関数外に「export default add;」を追加
  5. 「import { add } from './MyMath.js';」→「import add from './MyMath.js';」
  6. 「import add from './MyMath';」→「import add from './MyMath.js';」
  7. 下の関数の削除
// テスト実行
mocha.run(function(failures) {
  if (failures) {
    console.error('テストに失敗しました');
  } else {
    console.log('テストが完了しました');
  }
});

6月23日時点ではmochaにおいて自動生成ができていることを確認済み

ただimportや単体テストの実装方法でデバッグの内容に変化があります。

テスト内容はAzure OpenAIが自動生成できるレベルの関数なら問題なし!

まとめ

最初にも言った通り、完璧で究極のプロンプトではないので、実務の開発でそのまま使うのは何とも言えません。

完璧で究極のアイドルが完成したら、また改めてBlogを書きます。

須山先生の次回作に乞うご期待!!