Zerobase Dev Blog

コードネームYentry

Feather's Rules for Unit Tests

Michael Feathers writes:

A test is not a unit test if:

  • It talks to the database
  • It communicates across the network
  • It touches the file system
  • It can't run at the same time as any of your other unit tests
  • You have to do special things to your environment (such as editing config files) to run it.

Tests that do these things aren't bad. Often they are worth writing, and they can be written in a unit test harness. However, it is important to be able to separate them from true unit tests so that we can keep a set of tests that we can run fast whenever we make our changes.

SmalltalkのBDDフレームワーク(BabyMock、SSpec、Mocketry)

組み合わせは、

  • SUnit + BabyMock
  • SSpec + Mocketry

のいずれか。前者でいきたい。

BabyMock

SUnitと使う。

The recommended way of using BabyMock is to subclass BabyMockTestCase and write the test case in SUnit style. BabyMockTestCase extends from SUnit TestCase and has a instance variable called context.

なにこれカッコいいw

『実践テスト駆動開発』(Growing Object-Oriented Software Guided by Tests)に書かれていたことがまさに実践されてて感動。こんなにスラスラSmalltalkを書けるようになりたい。

この動画を見ても何やってるか分からない人は『実践テスト駆動開発』を読んだらいいです。あとPharo/Smalltalk特有の諸々を知りたければ Pharo by Example Chapter 6: The Pharo programming environment が分かりやすいですよ。

SSpec BDD testing framework

単体で機能する。

  1. Subclass SpecContext
  2. Create method category "specs". Only methods in the category named "specs" will be run as part of the spec, so you can use helper methods in other categories which will not run.
  3. Create some spec methods.
  4. Finally, to run the spec using the text runner:

TextSpecRunner verbose run: SampleContext. Transcript endEntry

Mocketry

SSpecと使う。

Mocketry - mock objects framework with SSpec functionality.

see: ESUG: TDD with Mocks in Smalltalk

I will introduce Mocketry framework that helps writing and maintaining mockist tests, and facilitates assertion specification.

実践テスト駆動開発 テストに導かれてオブジェクト指向ソフトウェアを育てる (Object Oriented SELECTION)

実践テスト駆動開発 テストに導かれてオブジェクト指向ソフトウェアを育てる (Object Oriented SELECTION)

ブログ名を「Zerobase Yentry開発ブログ」に変更しました

システム名をYentryにしました。

YenをentryするシステムなのでYentryですw

ドメイン yentry.com, yentry.org, yentry.jp を取得しました。

ブログ名も「ゼロベースの管理会計システムを開発するよ」から「Zerobase Yentry開発ブログ」に変更しました。

サイドバーの説明文も変更しました:

「社内取引制度」という独特な社内制度を持つゼロベース株式会社の管理会計システムを作っています。オブジェクト指向テスト駆動開発・継続的デリバリーなどの勉強も兼ねてSmalltalkで。

継続的デリバリーの導入

継続的デリバリーに、どこから手を付けるか、というところ。

広義の「開発環境」は、「アトラシアン固め」でいくか、「流行もの幕の内」でいくか、決めかねているところ。いまBacklog使ってますけど。

Atlassian Ondemandが手間なくて安そう。無料トライアルしてみる。

もはや言語はHeroku(ないし同等のPaaS)縛りでいいかな、という気もしています。うちは運用で付加価値を出せないので、コストでしかない。プロに任せたい。

なお、継続的デリバリーの導入を、全部自前でやる必要はない。プロジェクト初期にコンサルタントを雇って導入するほうが合理的だったりする。そればっかり繰り返しやっててノウハウを持ってる人を雇ってスピードを買いたい。そういうコンサルタントさんを探そう。

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

継続的デリバリー 信頼できるソフトウェアリリースのためのビルド・テスト・デプロイメントの自動化

文芸的テスト駆動開発 Literate Test-Driven Development

『実践テスト駆動開発』みたいな開発ログをとれないかな。DVCSのコミットログに十分説明的なコメントを残し、テストスイートの結果やスクリーンショットを添付する。それらを辿るためのビュアー(SourceTree的なもの?)がある。文芸的 (literate) TDD?

gitのコミットログへの添付ファイルは、そのコミットにファイルとして含むようにすればいいか。リポジトリルートに /commit_attachment てのを作ったらいいかも。

リポジトリから(ルポルタージュ的な意味での)「開発ドキュメント」を生成するツールはわりと簡単に作れそう。コミットログにMarkdownを書ければ。画像添付も ![]() で。ていうかそういうgitリポジトリブラウザすでにありそう。

Fine, make me a blog - My Re-thinking of Literate Programming

Literate Programming

DDD+TDD+DSLってliterate programming(文芸的プログラミング)の夢を復活させる感じ。

Redisによる透明な永続化層

RedisとMongoDBについて調べた』(2013-02-03)について考え直した。Redisによる透明な永続化層のアーキテクチャは、下記のようにシンプルになる。『データベースを使った永続化への復讐』(2013-02-03)に書いた理想に少しずつ近づいている。

エンティティと値オブジェクト

  • すべてがオブジェクトである。
  • オブジェクトには二種類ある。エンティティと値オブジェクトである。
  • エンティティは、ドメインで意味のあるIDを持つ(例:ISBN)。
  • 値オブジェクトは、エンティティを記述する。アイデンティティ(ID)はない。

Redisによる永続化の方針

  • エンティティだけを永続化する。
  • エンティティは、シリアライズして、Redisのhashに保存する。
  • 値オブジェクトは、その値とクラス情報のペアで表現する。
  • 他エンティティへの参照は、エンティティIDとクラス情報のペアで表現する。
  • エンティティと値オブジェクトの定義は、ソースコード上のアノテーションによってする。
  • インスタンスのIDをsetで管理し、ガベージ・コレクション方式で物理削除する。

永続化コード不要

  • リポジトリは自動生成する。永続化や復元の処理は書かなくてもよい。
  • 抽象的なインターフェイスを通じてリポジトリを利用できる。
  • リポジトリのユーザーは、リポジトリの実装(永続化にRedisを使っていることなど)を知らなくていい。環境設定がDependency Injectionされる。

普通なら、例えば、ドメインのEmployeeというクラスに対応したEmployeeRepositoryという抽象インターフェイスがあり、それを実装したEmployeeRepositoryForRedisがある。それらをすべて実装する。しかし、上述のように透明にしたい。

シリアル表現

クラス定義が

// Scala
class Employee (val name:String, val employer:Company)

インスタンス

// serialized entity
{ name:String = "Hideto Ishibashi", employer:Company = "123456789" } : Employee

のようになるイメージ。"123456789"CompanyのIDで、例えば会社法人等番号かもしれないし、システムへの登録時に自動生成されたシリアルナンバーかもしれない。

エンティティの永続化

エンティティをシリアライズしてhashに追加し、IDをsetに追加する。

  • store/update: HSET {class}.objects {objectID} {serializedObject} (*HSET)
  • also: SADD {class}.ids {objectID} (*SADD)

オブジェクトの復元

  • restore: HGET {class}.objects {objectID} (*HGET)

スキーマの問い合わせ

  • KEYS *.objects or KEYS *.ids (*KEYS)

ガベージ・コレクション

参照されていないIDをsetのdiffで求め、それらのエンティティをhashから削除し、IDをsetから削除する。

for each class:

  • all referenced IDs: SADD {class}.ref {objectID} {objectID} ... (*SADD)
  • all unreferenced IDs: SDIFFSTORE {class}.unref {class}.ids {class}.ref (*SDIFFSTORE)
  • fetch {count} unreferenced IDs: SRANDMEMBER {class}.unref {count} (*SRANDMEMBER)
  • delete unreferenced objects: HDEL {class}.objects {objectID} {objectID} ... (*HDEL)
  • also: SREM {class}.ids {objectID} {objectID} ... (*SREM)
  • clean up: DEL {class}.ref {class}.unref (*DEL)

ふつうに実行するとすべてのオブジェクトが消えてしまう。絶対の参照元が必要。例えば、マスターデータとユーザー(users)エンティティは、GCの対象とせず、明示的にsemanticに削除する。それ以外の「マスターでないエンティティ」かつ「どのユーザーにも紐づかないエンティティ」はGCされる。

考察

  • MongoDBと同じような使い方をすれば、同じように永続化できることがわかる。
  • Redisのほうが高性能だろうけれど、機能性ではMongoDBのほうが優れている。簡単な検索機能は単体で実現できるなど。
  • スケールアウトの観点では、どちらもshardingやclusteringに対応している。
  • オブジェクト永続化の観点で、RedisはMongoDBに対してトレードオフと言えるほど善戦しうるのかどうか、まだ分からない。MongoDBのほうがいいのかもしれない。

ももクロスライダーで測る Redis vs MongoDB on Heroku 第二弾 - from scratch』:

Redisはやっぱり高速でした。3点まとめです。
- ローカル環境の単純なアクセスではRedis は爆速。
- Heroku環境でもネットワークがボトルネックになるもののかなり高速を維持する。
- クライアントの同時接続数が増えるとRedis To Go アドオンのコネクション数が頭打ちになるため、場合によってはMongoよりも遅くなる。

追記

『Redisによる透明な永続化層』の余談と没ネタ - Memorandum by zerobase

GLASS (GemStone, Linux, Apache, Seaside, and Smalltalk) によるテスト駆動プロトタイピングへ

Play (Scala) + Redisで作ろうかと思っていたのですが、『データベースを使った永続化への復讐』などという考えに至り、もっぱらJava/Scalaの永続化技術について調べていたものの、これというものが見つかりませんでした。ところがSmalltalkにすごいものがありました。

Scalaを学んで関数型プログラミングパラダイムを魅力的だと考えていましたが、優先度はオブジェクト指向のほうが高いですから、SmalltalkベースのGLASSを調べています。永続化を気にせず、快適に「テスト駆動プロトタイピング」(TDP: test-driven prototyping)できそうです。

GLASS – GemStone, Linux, Apache, Seaside, and Smalltalk – provides a powerful new way to rapidly create and deploy desktop-like web applications. GLASS makes GemStone's transparent persistence, enterprise scalability, and legendary reliability available to web application developers. By using GLASS, you can focus on developing your application, and not spend any time on complex Object-Relational mapping or other persistence schemes. [GemStone Seaside | About]

なお、DSLのホスト言語としても、Smalltalkは十分な表現力を持っています。Scalaと同等の表現力は備えている、と評価しています。以下はMocketryのStateSpec DSLの例です:

5 should equals: 5.
5 should be an instance of: SmallInteger.
[1 / 0] should raise: ZeroDivide.
6 should not equals: 5.
5.0 should not be an instance of: SmallInteger.
[1 / 1] should not raise: ZeroDivide.

(cite)

Behaviour specificationsの書き方は以下のようになります:

OrderTests >> testIsFilledIfEnoughInWarehouse
    |order|
    order := Order on: 50 of: #product.
    [:warehouse|
        [order fillFrom: warehouse]
        should satisfy:
        [(warehouse has: 50 of: #product) will Return true.
         warehouse remove: 50 of: #product]
    ] runScenario.
    self assert: order isFilled.

(cite)

いろいろと調べたり学んだりするなかで、あらためて自分が「プロトタイピング」を製品構想・設計・開発の中核に置いているのだと再確認しました。デザイン思考的と言ってもよいのですが。

see also: - ウェブとデスクトップでの永続化の意味論

関連情報

GLASS/Seaside

TDD

Smalltalk