目次
・本日の成果・考え
・最後に
本日の成果
コントローラークラスのPGの際に、記載だけしてテストしてなかったエスケープ処理のUTを行いたいと思います。
先にUTしておけよと自分ながら思います。
テスト方針としては、
- エスケープされた文字列をアサート。
- エスケープ後の文字列がHTMLファイルでJavaScriptとして認識されるか
の上記2つを考えています。
※CommonFunctionのソースだけでは、エスケープ結果が想定通りかは確認できても、エスケープした結果がJavaScriptとして正しい処理なのかまでは確認できないからです。
LTのタイミングでも良いかもですが、面倒なのでこのタイミングで良いと思いました。
ソース
/**
* Java→JavaScriptへのエスケープ処理
* @param input 文字列型のチェック対象
* @return input エスケープ処理済み
*/
public static String escapeForJS(String input) {
if (input == null) return "\"\"";
String escaped = input
.replace("\\", "\\\\") //バックスラッシュ2つ
.replace("\"", "\\\"") //バックスラッシュ1つ
.replace("\n", "\\n") //改行\n
.replace("\r", "\\r") //改行CR
.replace("'", "\\'") //シングルクォーテーション
.replace("\t", "\\t"); //タブ
return "\"" + escaped + "\""; // JS文字列として囲う
}
テストソース
/**
* escapeForJS
* エスケープ処理
*/
@Test
public void testEscapeForJS() {
String className = new Object() {
}.getClass().getName();
String resultOutput = className + "のtestEscapeForJSメソッドのテスト";
out.println("**********************************************");
out.println(resultOutput + "が開始されました。");
try {
//通常文字列
String abc = CommonFunction.escapeForJS("abc");
System.out.println("パターン1.abc:"+abc);
//バックスラッシュ1つ
String abc2 = CommonFunction.escapeForJS("a\"b\"c");
System.out.println("パターン2.abc2:"+abc2);
//シングルクォーテーション
String abc3 = CommonFunction.escapeForJS("a'b'c");
System.out.println("パターン3.abc3:"+abc3);
//改行・CR
String abc4 = CommonFunction.escapeForJS("a\r\nb");
System.out.println("パターン4.abc4:"+abc4);
//タブ
String abc5 = CommonFunction.escapeForJS("a\tb");
System.out.println("パターン4.abc5:"+abc5);
//改行+文字列
String line = CommonFunction.escapeForJS("line1\nline2");
System.out.println("パターン5.line:"+line);
//バックスラッシュ2つ
String test = CommonFunction.escapeForJS("\\\\test");
System.out.println("パターン6.test:"+test);
//日本語(全角)
String hello = CommonFunction.escapeForJS("こんにちは?");
System.out.println("パターン7.hello:"+hello);
assertEquals("\"abc\"", abc);
assertEquals("\"a\\\"b\\\"c\"",abc2 );
assertEquals("\"a\\'b\\'c\"", abc3);
assertEquals("\"a\\r\\nb\"", abc4);
assertEquals("\"a\\tb\"", abc5);
assertEquals("\"line1\\nline2\"", line);
assertEquals("\"\\\\\\\\test\"", test);
assertEquals("\"こんにちは?\"", hello); // 全角記号
out.println(resultOutput + "が正常終了しました。");
} catch (Exception e) {
String resultError = String.format("エラーが発生しました。内容は{%s}", e);
out.println(resultError);
fail();
} finally {
out.println(resultOutput + "が終了しました。");
out.println("**********************************************");
}
}
テスト結果
**********************************************
app.test.CommonFunctionTest$10のtestEscapeForJSメソッドのテストが開始されました。
パターン1.abc:"abc"
パターン2.abc2:"a\"b\"c"
パターン3.abc3:"a\'b\'c"
パターン4.abc4:"a\r\nb"
パターン4.abc5:"a\tb"
パターン5.line:"line1\nline2"
パターン6.test:"\\\\test"
パターン7.hello:"こんにちは?"
app.test.CommonFunctionTest$10のtestEscapeForJSメソッドのテストが正常終了しました。
app.test.CommonFunctionTest$10のtestEscapeForJSメソッドのテストが終了しました。
**********************************************
エスケープ処理の動きは想定通りであることが確認できました。
調査用HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>エスケープ文字列テスト</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.message { background:white; margin: 5px 0; padding: 6px; border-radius: 4px; white-space: pre-wrap; }
</style>
</head>
<body>
<h2>JS側での文字列解釈テスト</h2>
<div id="chatArea"></div>
<script>
const chatArea = document.getElementById("chatArea");
function appendMsg(chunk) {
const msg = document.createElement("div");
msg.className = "message";
msg.textContent = chunk;
chatArea.appendChild(msg);
}
function showError(msg, error) {
const chatArea = document.getElementById("chatArea");
const errDiv = document.createElement("div");
errDiv.className = "message error";
errDiv.textContent = `[ERROR] ${msg}\n${error.message}`;
chatArea.appendChild(errDiv);
}
// Javaの escapeForJS によって加工された文字列リスト(Java側からコピー貼り付け)
const escapedStrings = [
"abc",
"a\"b\"c",
"a\'b\'c",
"a\r\nb",
"a\tb",
"line1\nline2",
"\\\\test",
"こんにちは?",
];
escapedStrings.forEach((str, index) => {
try {
appendMsg(`パターン${index + 1}: ${str}`);
} catch (e) {
showError(`パターン${index + 1}でエラー`, e);
}
});
</script>
</body>
</html>
不正な文字を混ぜるとJacaScript自体がエラーとなって、正常なものも表示できませんでした。ですので、単体テスト結果以外の不正文字を混ぜることはできませんでした。
補足として、どうして
assertEquals("\"a\\r\\nb\"", abc4);
がHTMLのリストで「"a\r\nb"」かというと、
/************
* メソッド名:受信チャンク表示
* 処理内容:受信したチャンク毎に表示処理を呼び出す
* @param chunk Difyからのレスポンス
/************/
private void appendChatChunk(String chunk) {
webEngine.call("appendMsg("+ CommonFunction.escapeForJS(chunk)+")");
実際の呼び出し時には、一度Javaの文字列としてcallメソッドに渡しており、
その時の内容はさっきのエスケープ処理で出力した形式と同じになっております。
呼び出し時の挙動
call("appendMsg("a\r\nb")";
画面として出力
結果
当然、問題なしですね笑。まあ一応テストの結果が、JavaScriptでも正しく読める形式になっていることは確認できたかと。
最後に
ここまでお付き合いありがとうございます。
前回から1週間ほどあきましたが、少しずつやっていこうと思います。
私ごとですが、前回の動作確認の様子を動画にして上長に共有しています。
感触としては良好で、あとはGoもらえば本格的に動き出せそうです。
それまでに、アプリの動作テストと、本番稼動の端末(Windows端末)への導入方法を確立させないとですね。
以上、ありがとうございました。