読者です 読者をやめる 読者になる 読者になる

おがさわらなるひこのオープンソースとかプログラミングとか印刷技術とか

おがさわらなるひこ @naru0ga が技術系で興味を持ったりなんだりしたことをたまーに書くブログです。最近はてなダイアリー放置しすぎて記事書くたびにはてな記法忘れるのではてなブログに移行しました。

クリエイティブ・コモンズ・ライセンス
特に断りがない場合は、本ブログの筆者によるコンテンツは クリエイティブ・コモンズ 表示 - 継承 4.0 国際 ライセンスの下に提供されています。

Notes DB で Testing Framework を活用するにはどうしたらいいのかな?

Notes Development 与太話

以下妄想。まとまっていないので「与太話」扱いで。

前振り

えーと Notes の開発言語 LotusScript の xUnit である LSUnit ってのがあります。
同じ名前のヤツで Java から外部でゴニョゴニョする奴があるみたいなんですが、私が使ってる奴は昔 http://amano001.at.infoseek.co.jp/LSUnit/ で公開されていたヤツで、LotusScript で書かれており、フォーム二個作ってファイルインポートするだけっていう気軽さが気に入ってます *1

が、気に入っているのはいいとして、なんか上手く使えないんですよねぇ。
前に LotusScript で小さな Wiki エンジンを書くという愚かな試みをやったことがあって、そういう外界から切り離されたヤツだとテストも上手く行くんですけどね。

では一般に、なにがテストのジャマをしてるのかっていうと、

Notes の設計要素「フォーム」は文書の雛型であり、それぞれのフィールドの定義もするのだけど、同時に見え方や操作(ボタンなど)も定義するので、どうしても UI の要素が出てきてしまい xUnit の得意分野である自動テストに分離できない。

という一点に尽きるんじゃないかと思うのでする。

じゃあそこを何とかするには、どういう DB 設計をすればいいか、と妄想してみた。

アクションを「見た目」に書かない

まぁこれは xUnit がどうこう以前に基本中の基本ですが……。

Notes においては「フォーム」とか「ビュー」という設計要素に「ボタン」とか「アクション」というのが配置できて、そのクリックイベントのハンドラを埋め込めるわけですが。

ここに処理をいきなり書いてしまうと、LSUnit による自動テストができない。
だってそのテストを発火させるためには「ボタンクリック」という操作を必要とするわけだから。当たり前ですよね。

だからイベントハンドラには関数/サブルーチン呼び出しだけ書き、関数/サブルーチンの呼び出しは「スクリプトライブラリ」という設計要素に追い出す。
これならファイルにエクスポート/インポートできるから、バージョン管理システムも使えるし嬉しいよね。

コンテキストに依存するもの (workspace / session / ……) の扱い

んで、Notes には「今のコンテキストに依存する」ものがたくさんあります。
例えば今の表示要素(ワークスペース)を意味する NotesUIWorkspace (以下 ws) とか、今のセッション情報を表す NotesSession (以下 session) とか。そしてその要素として ws.CurrentDocument (:= NotesUIDocument) とか session.CurrentDatabase (:= NotesDatabase) とかあって、さらにその要素として……とかあるわけで。

で、この手の「今のコンテキストに依存する」巨大なデータ構造を受け渡しして処理を回していると、どこでどのデータを弄ったのか訳が分からなくなる。副作用の固まり。あーもうやだやだ。

ということで、入り口はしょうがないとはして、最終的に末端の関数/サブルーチン群は本当に必要なパラメータを取り出して、それを元に呼び出すようにしないと。
関数/サブルーチン内部で、

  Dim ws As New NotesUIWorkspace

したり、それをずるずると持ちまわるのは止めないとダメですね。

これは関数/サブルーチンの再利用性向上にもつながるし、自動テストの観点からしても、「同じコンテクストを作ってあげる」より「同じパラメータだけ用意する」方がはるかに楽ですからね。

モデルとビューの分離

Notes でテストを書こうと思うと割と悩むのがこれ。あ、この「ビュー」ってのは下で出てくる Notes の設計要素じゃなくて、一般に MVC でいうところのビューを指します。

Notes の文書って持つべきデータとその見た目を両方持つのが設計思想なんですよね。そして、制御にしか使わない情報を非表示属性で隠すと。

また、文書の選択に使われる「ビュー」っていう設計要素 (以下 Notes ビュー) も、SELECT 文を Notes ビューごとにハードコードしちゃうし、見た目も規定するんでイロイロとテストで使うにはめんどいことがある。

つーことで、きっぱり「見た目に使う文書」と「制御に使う文書」を分離したらどうだろう、と思ったのだ。

前者はまさしく一般的な意味としてのビューとして用い、例えば「内部情報の最終更新時刻を分かりやすく表示する」とかそういうロジックはこっちに押しこむ。さっき書いたボタンとかの設計要素もこちら。値をテキストボックスで見せるのか、プルダウンメニューで選択するのか、チェックボックスにするのか、とかいうのもこっち。

後者は情報をただ情報として持つ。どう見せるかは意識しない。

実現の方法だけど、多分サブフォームを一個作って、そっちに「モデル」側の情報を全部押しこむ。
モデル側のフォームにはサブフォームをぺったん貼るだけ。
ビュー側のフォームはサブフォームを貼るのは同じだけど非表示属性にしておいて、実際に見せる/値を設定するフィールドについては、サブフォームのフィールドから値をもらってきてゴニョゴニョする。
んで、両者の値の交換 (アップデート) はそれぞれの PostSave() イベントでやる、とかそんな感じかなぁ。

Notes ビューの方も、設計で使うヤツはモデル側の文書を SELECT するようにしちゃって、もう見た目は考えない、と。

xUnit の setUp() で Notes の文書を作るときって、Notes 文書って見た目の完備性を考えなきゃいけないからけっこうめんどうで、こういうやり方をすれば少しはコストが減るんじゃないかな?

まとめ?

Web アプリケーションとかも、DB にアクセスするレイヤ、HTML を生成するレイヤはテストが難しいから、その分離ノウハウとかどっかにあると思うんですが、Notes についてはどうやるんだろう、という妄想でした。


もう作っちゃったDBについてはそれこそテスト書いてないので、テストを書きやすくするためにリファクタするとかおっかなくてできないんだけど、一から設計するのであればこういうことを考えてみようかな?

*1:が、Infoseek のサービス終了に伴って上記はデッドリンクになっちゃいました。今更 LS じゃないってことなんだろうなぁ……。