この記事は BABY JOB Advent Calendar 2023 の 6 日目の記事です。
まえおき
久しく投稿してなかったが、会社でアドベントカレンダーなる祭典を教えてもらい、乗っかってみる。
今回はJUnitにおけるパターン網羅の方法。
ではなく、パターン網羅したことをどのように残すかについて考えたので、披露したい。
今まで様々なプロジェクトに携わってきたが、こういった議論は一度もなかった。
もしかしたら、開発史上初の試みなのかもしれない。
そうなのであれば、私は喜んでそのパイオニアとなろう。
ただ、既に議論され尽くしていても、そっとしおいてほしい。
そして、パイオニア気分に浸らせたままにしてほしい。
問題提起
さて、このほど新しい環境で新しいプロジェクトに関わることになった。
そこでは、アジャイル開発が行われ、CICDが取り入れられて、モダンな開発が行われていた。
その中では当然単体テストツールによるテストがあるのだが、これがなかなか多い。
そして、パターン表などが残っていないため、コードから読み取る以外方法なない。
ここで考えた。
果たして、単体テストツールにおいてパターン表をどのように残すべきか?
思いつくものとしては以下のものだろう。
- Excel等でパターン表を作成し、GoogleドライブやSVNで管理する
- Excel等でパターン表を作成し、Gitで管理する
- テストクラスファイル中になんとかして残す
先2つについては、テストクラスファイルとファイルが分かれてしまうため、パターン表があることに気づかず、メンテナンスされない可能性がある。
そのため、Javadoc等にその情報の記載が必要だ。
また、テストクラスファイルとは別の場所で管理されているため、SVNやGoogleドライブ等の管理に依存してしまう。
(適切にバージョン管理されているのか、差分がきちんと管理できるのかなど)
かく言う私も今まではJavaコードをGitLab、パターン表をExcelで作成し、SVNで管理していた。
(先述した一つ目の選択肢を取っていた)
しかし、そこには以下のような課題があった。
- Javadocに
@see
や<a href="xxxx">
でパターン表との紐付けをしていても、メンテナンスがされないことがあった - パターン表がExcelのため、SVNでどこを修正したのかが分かりづらかった
- Microsoft Officeがメンバー分必要
そこで、私は最後のテストクラスファイル中になんとかして残す
方法を考えた結果、JavadocにHTMLのTableタグで表を残すことにした。
こうすることで、先に私が直面した3つの課題を以下のようにクリアできると考えた。
- Javadocにパターン表があるため、別途ファイルを探さなくてよい
- Javadocへの記載のため、テキストベースで差分を比較できる
- Microsoft Officeに依存せず、どの環境でも確認ができる
アプローチ
さて、本題である。
Javadoc上にHTMLのTableタグを残すとして、Javadoc上でのメンテナンスは少々面倒である。
そこで、会社で支給されているGoogle Workspaceを活用し、Javadoc上にTableタグを残すためのツールを手軽に作成しようと思う。
ただし、私はGoogle App Script(GAS)はほぼ触ったことがないため、ChatGPTを活用することとする。
まずは、ChatGPTに対して、以下のようなことを問いかける。
GASで以下の条件を満たすコードを作成して。
・A1セルからヘッダー行、データ行のあるHTMLのTableタグを取得する
・取得したTableタグを分解してスプレッドシートに書き込む
・関数はボタンを押したら実行される
すると、すぐに下記のコードを作成してもらえた。
function importTableFromHTML() {
// スプレッドシートを取得
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
// A1セルの値を取得
var cellValue = sheet.getRange("A1").getValue();
// HTMLからTableタグを正規表現で抽出
var tableRegex = /<table[\s\S]*?<\/table>/i;
var tableMatch = cellValue.match(tableRegex);
if (tableMatch) {
// 抽出したTableタグを分解して2次元配列に変換
var tableHtml = tableMatch[0];
var table = parseTable(tableHtml);
// スプレッドシートに書き込む
writeTableToSheet(table);
} else {
Logger.log("Table not found in the specified cell (A1).");
}
}
// Tableタグを分解して2次元配列に変換する関数
function parseTable(tableHtml) {
var table = [];
var tableRegex = /<tr[\s\S]*?<\/tr>/gi;
var rowMatches = tableHtml.match(tableRegex);
if (rowMatches) {
for (var i = 0; i < rowMatches.length; i++) {
var rowHtml = rowMatches[i];
var cellRegex = /<t[hd][\s\S]*?<\/t[hd]>/gi;
var cellMatches = rowHtml.match(cellRegex);
var row = [];
if (cellMatches) {
for (var j = 0; j < cellMatches.length; j++) {
var cellHtml = cellMatches[j];
var cellText = cellHtml.replace(/<[^>]*>/g, "");
row.push(cellText);
}
}
table.push(row);
}
}
return table;
}
// 2次元配列のデータをスプレッドシートに書き込む関数
function writeTableToSheet(table) {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
// データをクリア
sheet.clear();
// データを書き込む
for (var i = 0; i < table.length; i++) {
var row = table[i];
sheet.getRange(i + 1, 1, 1, row.length).setValues([row]);
}
Logger.log("Table imported successfully.");
}
うむ、動きそうだ。
とりあえず、Javadoc→スプレッドシートに関してはこれで良いとしよう。
では、この逆も作成してもらおう。
スプレッドシートの情報をhtmlのTableタグに変換してA1セルに出力するGASを作成して。
とお願いすると
function exportTableToHTML() {
// スプレッドシートを取得
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
// データを取得
var data = sheet.getDataRange().getValues();
// HTMLのTableタグに変換
var htmlTable = convertToHTMLTable(data);
// A1セルにHTMLを出力
sheet.getRange("A1").setValue(htmlTable);
Logger.log("Table exported to HTML successfully.");
}
// 2次元配列のデータをHTMLのTableタグに変換する関数
function convertToHTMLTable(data) {
var htmlTable = "<table border='1'>";
for (var i = 0; i < data.length; i++) {
htmlTable += "<tr>";
for (var j = 0; j < data[i].length; j++) {
htmlTable += "<td>" + data[i][j] + "</td>";
}
htmlTable += "</tr>";
}
htmlTable += "</table>";
return htmlTable;
}
うむ、これもいい感じだ。
あとは、スプレッドシートで1シートでI/Oさせるのか、HTMLシート/Tableシートに分けるのかで使い方を変えれば、良いかな。
まとめ
今回は、HTML→スプレッドシート、スプレッドシート→HTMLの部分のみを作成した。
ただ、実際コードから取得するのであれば、Javadocなどのものをそのまま貼り付けたいところなので、そこはまたどこかの機会でやるとします。