株式会社クリアコード > ククログ

ククログ


GaUnit新API

Gauche 用の単体テストフレームワーク GaUnit の0.1.4がリリースされました。

Gaucheには標準でgauche.testという単体テスト用のモジュールが付 属しています。このモジュールはテストスクリプトをはじめから順 に実行していくという素直なテスト実行方式を採用しています。こ の方式では一連のテストがどのように実行されていくかがわかりや すい半面、(場合によっては)以下のような問題があります。

  • 特定のテストのみを実行することができない
  • 各テスト毎にgoshプロセスを起動する必要がある (各テストが他のテストの影響をうけないようにするため)
  • テストを別々に走らせるため、すべてのテスト結果を見るため には、一度ファイル(テストログ)に保存しておいてあとで確 認する、としなければいけない(テストを走らせる前にテスト ログを削除して前のテスト結果を削除する必要がある)

また、gauche.testはテストの成功、失敗にかかわらずテスト結果が 常に冗長であるという問題があります。テストが失敗した項目(修正 の必要がある項目)についてのみ情報を詳細する方が、より合理的で しょう。

ということで、GaUnitです。GaUnitは xUnit系の単体テ ストフレームワークで、上記のgauche.testの問題を解決します。 また、テスト失敗時以外はテスト結果に余計な情報を出力しないため 無駄がありません。そんなGaUnitが今回のリリース(正確には1つ前の 0.1.3)からより書きやすいAPIを提供して、以下の2点を行うだけで すむようになりました。

  • テスト用のモジュールを作成
  • その中でtest-始まりの手続きを定義

実際のコードは以下のようになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(define-module test-your-library
  (extend test.unit.test-case)
  (use your-library))
(select-module test-your-module)

(define (test-your-module-procedure1)
  (assert-equal "Good!" (your-module-procedure1))
  ...
  #f)

(define (test-your-module-procedure2)
  (assert-equal 29 (your-module-procedure2))
  ...
  #f)

(provide "test-your-module")

最後にtest-*手続きの最後に#fを付けているのは末尾再帰の最適化 でバックトレースを落とさないようにするためです。

新しいAPIでは、普通のGaucheライブラリと同じようにテストを書 けます。テストのために覚えることと言えば、どのassert-*を使お うかということくらいです。これは、普段の別のライブラリを用い た開発と同じですね。

GaUnitでのテスト書き方をもっと知りたい人はチュートリアル を読んでください。

タグ: テスト
2008-06-04

UxUで始めるFirefoxアドオンの自動テスト

Firefox用アドオンやXULRunnerアプリケーションなどのいわゆるXULアプリケーションは、ロジック部を主にJavaScriptで記述するため、script.aculo.usのテスト関連機能などJavaScript用のテストツールを使って自動テストを行えます。しかし、一般的なJavaScript用のテストツールはWebアプリケーションをテストすることを主眼において開発されているため、利用できる機能に制限があったり、HTMLではなくXULを使用するXULアプリケーションのテストでは不具合が生じたりする場合があります。

UxU(UnitTest.XUL)は、著名なXULアプリケーション開発支援ツールであるMozLabをベースにクリアコードで開発を行っている自動テスト実行ツールです。FirefoxやThunderbirdなどのXULアプリケーション上での利用を前提としているため、前述のような制限や問題を気にすることなく自動テストを記述できる、便利なヘルパーメソッドを利用できる、などの特長があります。

テストの記述方法やヘルパーメソッドの一覧はUxUの紹介ページに情報がありますが、ここではFirefox用アドオンのXUL/Migemoのテストを実例として示しながら、UxUによる自動テストの方法について簡単にご紹介をしたいと思います。Subversionのリポジトリ内にテストのサンプル用に用意されたタグがありますので、まずはこちらから必要なファイル一式をチェックアウトしておいてください。

クラスのユニットテスト

まずは最も簡単な例として、「tests」→「unit」とフォルダを辿った中にあるdocShellIterator.test.jsを見てみましょう。

XUL/MigemoはFirefoxのページ内検索を拡張するアドオンですので、フレーム内に検索語句が見つからなかったときは、子フレームや親フレームを検索対象として自動的に再検索を行うといった処理が必要になります。docShellIterator.test.jsでは、このための処理を担当するクラス「DocShellIterator」が正しく機能するかどうかをテストしています。

include
1
utils.include('../../components/pXMigemoFind.js', null, 'Shift_JIS');

冒頭では、ヘルパーメソッドのutils.includeを使って、DocShellIteratorクラスが定義されているファイルpXMigemoFind.jsの内容を取り込んでいます。第3引数で読み込むファイルのエンコーディングを指定していますが、これはファイルの中に含まれる日本語のコメントがShift_JISになっているためです。

なお、何らかの事情でそのままファイルをincludeできない(includeするとまずい)場合には、ファイルの内容をテキストとして読み込んで加工した後に評価するという方法もあります。上の例は、以下のように書き換えても同様に動作します。

1
2
3
4
5
var extract;
eval('extract = function() { '+
     utils.readFrom('../../components/pXMigemoFind.js') +
     '; return DocShellIterator }');
var DocShellIterator = extract();

utils.readFromはファイルの内容を文字列として返しますので、replaceなどを使って邪魔な部分を消してやれば、そのままではincludeできないファイルから必要な部分だけを取り出して評価できます。

テストケースの定義

このファイルにはテストケースが一つだけ定義されています。テストの前処理(setUp)と後処理(tearDown)は以下の通りです。

1
2
3
4
5
6
7
8
9
10
11
12
var DocShellIteratorTest = new TestCase('DocShellIteratorのユニットテスト');

DocShellIteratorTest.tests = {

  setUp : function() {
    yield Do(utils.loadURI('../res/frameTest.html'));
  },

  tearDown : function() {
    iterator.destroy();
    yield Do(utils.loadURI());
  },

setUpとtearDownは、それぞれのテストを実行する前と後に毎回実行されます。このテストケースでは、テスト実行前に「テストに使用するフレームにHTMLファイルを読み込む」という処理を行い、テスト終了後に「フレームに空のページを読み込んで内容を破棄する」という処理を行うことで、毎回必ずクリーンなテスト環境を準備するようにしています。

setUpの中では、「フレームに指定したページを読み込み、読み込みが完了するのを待って次に進む」といった処理待ちを行うために、ヘルパーメソッドとyield式を使用しています。yieldは本来はJavaScript 1.7で導入されたジェネレータのための物ですが、UxUではこれを応用して処理待ちを実現しています。JavaScriptで処理待ちというと、タイマーやonloadのようなイベントハンドラを使う方法が真っ先に思い浮かぶと思いますが、yield式を使用すれば、それらの場合に比べて処理の流れをフラットに記述することができます。

テストの内容

個々のテストの定義を見てみましょう。以下のテストでは、DocShellIteratorクラスのインスタンスを生成して、検索対象のフレームを移動する処理を実際に行い、処理結果が期待されたものと等しいかどうかをテストしています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
'前方検索': function() {
  // 1番目のフレームを初期状態としてインスタンス生成。
  iterator = new DocShellIterator(content.frames[0], false);
  // 初期化は成功したか?
  assert.initialized(iterator, content.frames[0]);

  // 次のフレームに移動するメソッドを実行。
  assert.isTrue(iterator.iterateNext());
  // フォーカスは正常に2番目のフレームに移動したか?
  assert.focus(iterator, content.frames[1]);
  assert.isFalse(iterator.isInitial);
  // もう一度フレームを移動。
  // 3番目のフレームは無いので、一巡して最上位のフレームにフォーカスする。
  assert.isTrue(iterator.iterateNext());
  // フォーカスは正常に最上位のフレームに移動したか?
  assert.focus(iterator, content);
  assert.isFalse(iterator.isInitial);
  // もう一度フレームを移動。1番目のフレームに戻る。
  assert.isTrue(iterator.iterateNext());
  // フォーカスは正常に1番目のフレームに移動したか?
  assert.focus(iterator, content.frames[0]);
},

処理が成功したかどうかを確認する手続きを、アサーション(宣言)と呼びます。assert.isTrue、assert.isFalse、assert.equalsなどのアサーション用ヘルパーメソッドは、実行されると渡された値を評価します。実際に渡された値が期待された値と等しければそのまま処理を続行しますが、値が異なっていた場合は「アサーションに失敗した」という内容の例外を発生させてテストの実行が中断されます。これらの例外やメソッド実行時に発生した未知の例外はUxUのインターフェース上に逐次表示され、例外が発生した行のスタックトレースを後で辿ることができるため、どの時点で問題が起こったのか、どこまでは予想通りに正常に動いたのかを詳しく調べてデバッグに役立てられます。

UxUのページのアサーション一覧にないassert.initializedやassert.focusはこのテスト専用に定義したカスタムアサーションです。その実体は、このファイルの冒頭でassertオブジェクトに追加されている新しいメソッドで、以下のように、内部でより単純なアサーションを複数行っています。

1
2
3
4
5
6
7
8
9
assert.focus = function(aIterator, aFrame) {
  assert.equals(aFrame.location.href, aIterator.view.location.href);
  assert.equals(aFrame, aIterator.view);
  assert.equals(aFrame.document, aIterator.document);
  assert.equals(aFrame.document.body, aIterator.body);
  var docShell = getDocShellFromFrame(aFrame);
  assert.docShellEquals(docShell, aIterator.current);
  assert.isTrue(aIterator.isFindable);
}

複数の条件を満たしているかどうかを確認する必要がある場合、そのままテストを記述すると、同じコードがテストケースの中に大量に並んでしまいます。そういった一連の処理はカスタムアサーションとしてまとめておけば、テストケースの内容を綺麗に見やすくできます。

テストの実行

テストの実行は、MozRepl互換のUxUサーバを起動してコンソールから接続して行うか、MozUnit互換のテストランナーを起動して行います。「ツール」メニューから「UnitTest.XUL」→「MozUnitテストランナー」と辿り、テスト実行用のGUIを起動します。

UxUのテスト実行用GUIは、テストケースのファイルのドラッグ&ドロップを受け付けます。実行したいテストのファイル(docShellIterator.test.js)をウィンドウにドラッグ&ドロップすると「作業中のファイル」欄のファイルのパスがドロップされたファイルのものになりますので、後は「実行」ボタンを押すだけで自動テストを実行できます。

テストの現在の実行状況はプログレスメーターで表示されます。アサーションに失敗したり予期しないエラーが起こったりするとプログレスメーターが赤くなりますが、正常にテストが成功すれば緑色のままです。すべてのテストケースの実行結果が緑となることを目指して開発や修正を進めていきましょう。

まとめ

このように、一連の処理と期待される結果をまとめておき、自動的にテストできるようにしておくと、開発した機能がきちんと動作しているかどうかを人手を煩わさず機械的に確かめられます。一通りのテストケースを作成するにはそれなりの手間と時間を要しますが、一度作成しておけばテストは何度でも簡単に実行できますので、うっかりミスによるエンバグを未然に防ぐ*1ことができます。

Firefox用のアドオンはFirefox自身が持っている機能と連携して動作するように作られることが多く、自動テストを作るのはなかなか大変です。UxUには処理待ち機能をはじめとした多くの便利な機能があり、「このような操作を行った時に、こういう結果になる」といった人間の操作をシミュレートする形の複雑なテストでも、処理の流れを追いやすい形で記述できます。Firefox用アドオンの自動テスト作成にはまさにうってつけと言えるでしょう。

*1 エンバグしてしまってもすぐにそれに気がつくことができる

タグ: Mozilla | テスト | UxU
2008-06-11

ActiveLdap 1.0.1リリース

LDAPのエントリを ActiveRecord風のAPIでア クセスするためのライブラリ、 ActiveLdap 1.0.1がリリースされました。

ActiveLdapとは

ActiveRecord風のAPIとは1エントリを1オブジェクトとして扱える ということです。例えば、ユーザの説明を変更する場合は以下のよ うになります。

1
2
3
alice = User.find("alice")
alice.description = "New user"
alice.save!

ActiveRecordと同じように、各クラス間の関係を設定して便利にア クセスすることもできます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class User < ActiveLdap::Base
  belongs_to :groups, :many => "memberUid"
end

class Group < ActiveLdap::Base
  has_many :users, :wrap => "memberUid"
end

alice = User.find("alice")
alice.groups # => [Group("friend"), Group("office"), ...]
alice.groups << Group.find("home")
alice.groups # => [Group("friend"), Group("office"), Group("home"), ...]

friend = Group.find("friend")
friend.users # => [User("alice"), User("bob"), ...]

ActiveRecordと同じように、Ruby on Railsと使用することもでき ます。

% script/plugin install http://ruby-activeldap.googlecode.com/svn/tags/r1.0.1/rails/plugin/active_ldap
% script/generate scaffold_active_ldap
% vim config/ldap.yml

ActiveLdapは以下のライブラリをバックエンドとして利用できます。

  • Ruby/LDAP
    • 拡張ライブラリ(速い、インストールが大変かもしれない)
  • Net::LDAP
    • Rubyのみで実装(遅い、インストールは簡単)
    • 2008/06/17時点の最新版0.0.4では動かない。 Subversion 版を利用する必要がある。
  • JNDIのLDAPサービスプロバイダ (実験的)
    • JRubyでのみ利用可能。

ベンチマーク

以下はActiveLdapに付属するベンチマークの結果です。ベンチマー クでは100エントリを検索しています。「Rehearsal(リハーサル)」 を行って、それぞれ2回ずつ実行しているのは、以前はキャッシュ などで2回目以降の結果がよくなることなどがあったためです。現 在はあまり意味がありませんが、歴史的に残っています。

% ruby benchmark/bench-al.rb --config benchmark/config.yaml
Populating...
Rehearsal -------------------------------------------------------
  1x: AL              0.080000   0.010000   0.090000 (  0.098738)
  1x: AL(No Obj)      0.010000   0.000000   0.010000 (  0.016623)
  1x: LDAP            0.000000   0.000000   0.000000 (  0.008674)
  1x: Net::LDAP       0.030000   0.000000   0.030000 (  0.045199)
---------------------------------------------- total: 0.130000sec

                          user     system      total        real
  1x: AL              0.080000   0.020000   0.100000 (  0.100959)
  1x: AL(No Obj)      0.010000   0.010000   0.020000 (  0.020697)
  1x: LDAP            0.000000   0.000000   0.000000 (  0.010129)
  1x: Net::LDAP       0.030000   0.000000   0.030000 (  0.042075)
Entries processed by Ruby/ActiveLdap: 100
Entries processed by Ruby/ActiveLdap (without object creation): 100
Entries processed by Ruby/LDAP: 100
Entries processed by Net::LDAP: 100
Cleaning...

各項目はそれぞれ以下の通りです。

  • AL: Ruby/LDAPバックエンドのActiveLdapで検索を行い、各エ ントリをオブジェクト化する(ActiveRecord風のAPIを利用す る場合)
  • AL(No Obj): Ruby/LDAPバックエンドのActiveLdapで検索を行 い、各エントリの結果をオブジェクト化しない(エントリを配 列やハッシュなどを使って表現)
  • LDAP: Ruby/LDAPで検索を行う
  • Net::LDAP: Net::LDAPで検索を行う

上記の結果からは以下のことが言えます。

  • 本当に速度が重要な場合にはRuby/LDAPを直接利用する方がよ い。
  • 利用できるならば、Net::LDAPよりもRuby/LDAPバックエンドを 利用した方がよい。
  • Net::LDAPを直接利用するよりも、オブジェクト化しない ActiveLdap + Ruby/LDAPバックエンドの方が速い。

多くの場合、1度に100エントリを処理することは少ないでしょう。 そのため、通常はActiveLdapで各エントリをオブジェクト化しても 問題は少ないといえます。

もし、1度に多くのエントリを扱う場合で、読み込み専用ならば、 オブジェクト化しない方法で利用することでパフォーマンスを改善 することができます。

まとめ

ActiveLdapを利用することでLDAPのエントリをオブジェクト指向的 なAPIで自然に処理することができます。

ActiveLdapは複数のLDAPバックエンドに対応しており、Rubyがイン ストールされている環境さえあれば動かすこともできます。 (Net::LDAPバックエンド使用時。ただしそんなに速くない)また、 JRubyでもほとんどの機能が動きます。

もし、Ruby/LDAPを利用できる環境であれば、Net::LDAPを直接利用 するよりも、ActiveLdap + Ruby/LDAPバックエンドを利用した方が よりオブジェクト指向らしいAPIでLDAPのエントリを操作できます。 また、速度が要求される場合であれば、オブジェクト化を行わない (オブジェクト指向らしいAPIを利用しない)ことにより、より高 速にLDAPのエントリを読み込むことができます。

タグ: Ruby
2008-06-15

Pikzie 0.9.2リリース

Python用の単体テストフレームワークである Pikzie 0.9.2がリリースされま した。

以下のようにeasy_installでインストールできます。

% sudo easy_install Pikzie

類似のテスティングフレームワーク

Python用の単体テストフレームワークとしてはPythonに標準添付さ れている unittest や、unittestよりも柔軟にテストが書ける py.testなど があります。

また、unittest自体を拡張してより柔軟にテストが書けるようにし た nose もあります。noseはプラグイン方式をサポートしており、柔軟にテ ストが書ける機能以外にも、プラグインとして以下のような機能を 提供しています。

  • テストを省略するskipの追加
  • カバレッジをレポートする機能
  • doctest のサポート
  • ...

また、BDD用のテスティングフレームワークとしては pyspecがあります。

Pikzieを使う理由

上記に挙げた既存の単体テストフレームワークには共通して以下の ような問題点があります*1

  • 期待値(expected)と実測値(actual)の違いを判断することが困 難な出力結果

上記の問題を解決することがPikzieを使うもっとも大きな理由にな ります。これは、テストの失敗結果を使いながらデバッグすること が多いからです。

テストを書くことの重要性、テストの書き方*2などを解説しているものはよくみますが、 テストが失敗してそれを修正していく過程を書いているものはなか なかみかけません。しかし、テストは一度成功したらそれ以降も成 功し続けるわけではないのです。開発の途中でテストは何度も何度 も失敗します。例えば、以下のような場合に既存のテストが失敗す るかもしれません。

  • 新しい機能を追加するとき
  • 既存の機能を改良せずにパフォーマンスを改良するとき
  • 仕様が変わったとき
  • 仕様を変更せずに実装を修正するとき
  • テスト用データを追加・変更したとき
  • 依存しているライブラリのバージョンがあがったとき

つまり、新しいテスト・機能を開発していくときに既存のテストが 失敗することは当たり前のことです。

テスト結果の表示

以下はunittestで書かれたテストです。

1
2
3
4
5
6
7
8
9
10
# unittest-test.py
import unittest

class FailTestCase(unittest.TestCase):
    def test_fail(self):
        x = 1110111011110
        self.assertEquals(x + 100000, 1111111011111)

if __name__ == '__main__':
    unittest.main()

このテストの実行結果は以下のようになります。

% python unittest-test.py
F
======================================================================
FAIL: test_fail (__main__.FailTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "unittest-test.py", line 6, in test_fail
    self.assertEquals(x + 100000, 1111111011111)
AssertionError: 1110111111110 != 1111111011111

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

期待値と実測値は以下のように表示されています。

AssertionError: 1110111111110 != 1111111011111

どこが違うのかがわかりにくいのがわかると思います。

noseを用いた場合もテストの書き方は変わりません。ただし、テス トを実行するためにnosetestsコマンドを使う必要があります。

% nosetests unittest-test.py
F
======================================================================
FAIL: test_fail (unittest-test.FailTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/unittest-test.py", line 6, in test_fail
    self.assertEquals(x + 100000, 1111111011111)
AssertionError: 1110111111110 != 1111111011111

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)

以下はpy.testで書いたテストです。

1
2
3
4
5
6
# pytest-test.py
import py

def test_fail():
    x = 1110111011110
    assert x + 100000 == 1111111011111

このテストの実行結果は以下のようになります。py.testコマンド で起動します。

% /tmp/py-0.9.1/py/bin/py.test pytest-test.py
inserting into sys.path: /tmp/py-0.9.1
============================= test process starts =============================
executable:   /usr/bin/python  (2.5.2-final-0)
using py lib: /tmp/py-0.9.1/py <rev unknown>

pytest-test.py[1] F

_______________________________________________________________________________
____________________________ entrypoint: test_fail ____________________________

    def test_fail():
        x = 1110111011110
E       assert x + 100000 == 1111111011111
>       assert (1110111011110 + 100000) == 1111111011111

[/tmp/pytest-test.py:5]
_______________________________________________________________________________
================== tests finished: 1 failed in 0.08 seconds ===================

unittestと違ってxの値が展開されていますが、 (1110111011110 + 100000)の結果は表示されていません。こ のため、実際の値が期待値とどう違うのかがわかりません。

最後にPikzieで書いたテストです。

1
2
3
4
5
6
7
# pikzie-test.py
import pikzie

class TestFail(pikzie.TestCase):
    def test_fail(self):
        x = 1110111011110
        self.assert_equal(1111111011111, x + 100000)

実行結果は以下の通りです。特別なテスト起動コマンドは必要あり ません。

% python pikzie-test.py
F

1) Failure: TestFail.test_fail: self.assert_equal(1111111011111, x + 100000)
pikzie-test.py:6: self.assert_equal(1111111011111, x + 100000)
expected: <1111111011111>
 but was: <1110111111110>

Finished in 0.005 seconds

1 test(s), 0 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 notification(s)

以下のように期待値と実測値が並べて表示されるので、違いをみつ けやすくなります。

expected: <1111111011111>
 but was: <1110111111110>

また、場合によってはdiffが表示されます。

1
2
3
4
5
6
# pikzie-test-diff.py
import pikzie

class TestDiff(pikzie.TestCase):
    def test_diff(self):
        self.assert_equal("aaaaaxaaaaaaaaa", "aaaaaoaaaaaaaaa")

実行結果です。

% python pikzie-test-diff.py
F

1) Failure: TestDiff.test_diff: self.assert_equal("aaaaaxaaaaaaaaa", "aaaaaoaaaaaaaaa")
pikzie-test-diff.py:5: self.assert_equal("aaaaaxaaaaaaaaa", "aaaaaoaaaaaaaaa")
expected: <'aaaaaxaaaaaaaaa'>
 but was: <'aaaaaoaaaaaaaaa'>

diff:
- aaaaaxaaaaaaaaa
?      ^
+ aaaaaoaaaaaaaaa
?      ^

Finished in 0.033 seconds

1 test(s), 0 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 notification(s)

また、長い行の場合は折り返してdiffを表示します。

1
2
3
4
5
6
7
8
9
10
11
# pikzie-test-diff-long
import pikzie

class TestDiffLong(pikzie.TestCase):
    def test_diff_long(self):
        self.assert_equal("ppppppppppppppppppppppyyyyyyyyyyyyyyy"
                          "ttttttttttttttttttttttttttttttttttttt"
                          "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhon",
                          "ppppppppppppppppppppppyyyyyyyyyyyyyyy"
                          "ttttttttttttttttttttttttttttttttttttt"
                          "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhoon")

実行結果です。

% python pikzie-test-diff-long.py
F

1) Failure: TestDiffLong.test_diff_long: "ppppppppppppppppppppppyyyyyyyyyyyyyyy"
pikzie-test-diff-long.py:8: "ppppppppppppppppppppppyyyyyyyyyyyyyyy"
expected: <'ppppppppppppppppppppppyyyyyyyyyyyyyyyttttttttttttttttttttttttttttttttttttthhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhon'>
 but was: <'ppppppppppppppppppppppyyyyyyyyyyyyyyyttttttttttttttttttttttttttttttttttttthhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhoon'>

diff:
- ppppppppppppppppppppppyyyyyyyyyyyyyyyttttttttttttttttttttttttttttttttttttthhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhon
?                                                                                                             ^
+ ppppppppppppppppppppppyyyyyyyyyyyyyyyttttttttttttttttttttttttttttttttttttthhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhoon
?                                                                                                             ^

folded diff:
  ppppppppppppppppppppppyyyyyyyyyyyyyyyttttttttttttttttttttttttttttttttttttthhhh
- hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhon
? -
+ hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhoon
?                                +

Finished in 0.008 seconds

1 test(s), 0 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 notification(s)

diffの後に、長い行を折り返した結果に対するdiffも表示されてい ます。期待値・実測値が長い文字列で表現される場合は、折り返し た結果のdiffを見た方が異なる部分を見つけやすくなります。

まとめ

テストは頻繁に失敗します。Pikzieはテストの修正に必要な情報を できるだけ多く、簡潔に表示します。これは、テストの修正を迅速 に行うために大事なことです。

ここでは書きませんでしたが、もちろん、Pikzieは他のテスティン グフレームワークと同じように柔軟にテストを書くことができます。

*1 unittestは命名規則がCamelCaseで PEP 8 -- Style Guide for Python Codeから外れ ているという問題もあります。

*2 例えば、テストの粒 度やテスト駆動開発など

タグ: テスト
2008-06-27

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|