taisablog

taisa's engineer blog

「 月別アーカイブ:2016年12月 」 一覧

PHPのモッキンフレームワークPhakeの使い方

2016/12/20   -PHP
 , , , ,

前回に引き続きPHPUnit関連の記事です。今回はPHPのモッキンフレームワークであるPhakeの使い方を確認します。 Phakeについて 作者は? Mike Livelyという方のようです。 twitterGitHub Phakeって? こちらですね。 PhakeGitHub ファイル構成 一応ファイル構成を載せておきます。 . ├── Phake │   ├── Annotation │   │   ├── MockInitializer.php │   │   └── Reader.php │   ├── CallRecorder │   │   ├── Call.php │   │   ├── CallExpectation.php │   │   ├── CallInfo.php │   │   ├── IVerificationFailureHandler.php │   │   ├── IVerifierMode.php │   │   ├── OrderVerifier.php │   │   ├── Position.php │   │   ├── Recorder.php │   │   ├── Verifier.php │   │   ├── VerifierMode │   │   │   ├── AtLeast.php │   │   │   ├── AtMost.php │   │   │   ├── Result.php │   │   │   └── Times.php │   │   └── VerifierResult.php │   ├── ClassGenerator │   │   ├── EvalLoader.php │   …

PHPUnitのモックオブジェクトの使い方を仕組みから理解する

前回はPHPUnitのメイン処理を確認しました。今回はPHPUnitデフォルトのモックオブジェクトの仕組みを確認してみます。公式ドキュメントでは、第9章 テストダブルが該当箇所となります。 PHPUnitのモックオブジェクトについて PHPUnitは以下のような構成ですが、その中の「phpunit-mock-objects」がPHPUnitデフォルトのモックライブラリとなります。 phpunitphp-code-coveragephp-file-iteratorphp-text-templatephp-timerphp-token-streamphpunitphpunit-mock-objects ← これ 構成 PHPUnitモックオブジェクトのファイル構成は以下の通りです。 ├── Builder │   ├── Identity.php │   ├── InvocationMocker.php │   ├── Match.php │   ├── MethodNameMatch.php │   ├── Namespace.php │   ├── ParametersMatch.php │   └── Stub.php ├── Exception │   ├── BadMethodCallException.php │   ├── Exception.php │   └── RuntimeException.php ├── Generator │   ├── deprecation.tpl.dist │   ├── 省略… ├── Generator.php ├── Invocation │   ├── Object.php │   └── Static.php ├── Invocation.php ├── InvocationMocker.php ├── Invokable.php ├── Matcher │   ├── AnyInvokedCount.php │   ├── AnyParameters.php │   ├── ConsecutiveParameters.php │   ├── Invocation.php │   ├── InvokedAtIndex.php │   ├── InvokedAtLeastCount.php │   ├── InvokedAtLeastOnce.php │   ├── InvokedAtMostCount.php │   ├── InvokedCount.php │   ├── InvokedRecorder.php │   …

PHPUnitの使い方を仕組みから理解する

2016/12/05   -PHP
 ,

ここ数年仕事ではPHPを使って開発をしていますが、最近品質について考える機会が増えたこともあり、これを機にPHPUnitと周辺のモジュールの仕組みを理解してより楽にテストができるようにしたいと思います。 PHPUnitは? Sebastian Bergmann Created PHPUnit. Co-Founded thePHP.cc. Helps PHP developers build better software. PHPUnitの作者は、Sebastian Bergmannという方でthePHP.ccのファウンダーのようです。関連情報は以下にて確認してみてください。 TwitterアカウントPHPUnit GithubPHPUnitマニュアル PHPUnitの構成 PHPUnitは以下のような構成になっています。 phpunit php-code-coverage php-file-iterator php-text-template php-timer php-token-stream phpunit phpunit-mock-objects これらはGitHub上ではそれぞれ別々のリポジトリに分かれていますが、phpunitが本体でそれ以外はデフォルトの関連ライブラリという位置づけになるかと思います。 PHPUnitのsrc構成 モックオブジェクトなどを除いたphpunitだけのパッケージとクラス構成を見てみるとこんな感じになります。 ├── Exception.php ├── Extensions │   ├── GroupTestSuite.php │   ├── PhptTestCase.php │   ├── PhptTestSuite.php │   ├── RepeatedTest.php │   ├── TestDecorator.php │   └── TicketListener.php ├── ForwardCompatibility │   └── TestCase.php ├── Framework │   ├── Assert │   │   └── Functions.php │   ├── Assert.php │   ├── AssertionFailedError.php │   ├── BaseTestListener.php │   ├── CodeCoverageException.php │   ├── Constraint │   │   ├── And.php │   │   ├── ArrayHasKey.php │   │   ├── ArraySubset.php │   │   ├── Attribute.php │   │   …

FlaskをBottleと比較した雑感

2016/12/05   -Python
 ,

以前Bottleのソースをチェックしてみた流れでFlaskも見てみた。結論から言うとBottleと大して変わらん。もちろんFlaskのほうがコード量が多く多少リッチではあるもののざっくり機能ベースで言うと大して変わらんのです。そもそも両方ともマイクロWebフレームワークが売りなので当たり前といえば当たり前ですが、歴史的にもBottleが2009年リリースでFlaskが2010年4月1日(エイプリルフールのネタとして)リリースと、名前もBottleに対抗してFlask(フラスコ)という名前をつけたということなので、もともとがBottleっぽいフレームワークを遊びで作ってみたという感じなんだと思います。それっぽいことはwikiに書かれています。 [Bottle Wiki](https://en.wikipedia.org/wiki/Bottle_(web_framework)Flask Wiki クラス構成に関しては以下にある通りで、Bottleが1ファイル構成でFlaskは機能にファイルが分かれてる構成という違いはあるものの、機能自体にほとんど違いはありません。ただFlaskはBlueprintという大規模アプリ対応の機能があります。なお、使い方については公式ドキュメントとサンプルコードも複数用意されているのでアプリの構成や使い方が確認しやすいです。

Git 2.9でdiffがちょっとかしこくなった

2016/12/05   -Git
 

ちょっと前にはなるがGit 2.9がリリースされてdiffがちょっとかしこくなった。 リリースノート Git 2.9 Release Notes 記事 Git 2.9 has been releasedGit2.9のキレイなdiffを出すためのconfig diffをいままでよりいい感じにする git diffに「–compaction-heuristic」オプションを追加するかgit configに「diff.compactionHeuristic true」を設定することで、これまでdiffがコンフリクト起こして正しく出せてなかったケースをカバーするようになった。今回のリリースではオプションつけるか設定変更する必要があるけど後々デフォルトになる予定とのこと。ちなみにほんとかな?と記事のソースで新旧バージョンで比較してみたところしっかり解消されてました。はいすいません。オプションつける場合 git diff –compaction-heuristic コンフィグに設定する場合 git config –global diff.compactionHeuristic true ハイライトもこれまでよりいい感じにできる diff-highlightでは同行の文字変更を見やすくするというものだけどこれ自体は以前からあった。ではどこが変わったかというと、これまでごく一部でハイライトできなかったけどそれ解消したよってことだと思う。でこっちも設定変える必要あるよとのこと。設定がなければ追加 git config –global pager.log ‘diff-highlight | less’ git config –global pager.show ‘diff-highlight | less’ git config –global pager.diff ‘diff-highlight | less’ 今回のリリースで追加された新しい設定 git config interactive.diffFilter diff-highlight ということで今回のリリースによって.gitconfigに設定が追加された。 [diff] compactionHeuristic = true [interactive] diffFilter = diff-highlight

Python Bottleのソースを読む ユニットテスト編

2016/12/05   -Python
 ,

Python Bottleのソースを読む テスト編前回までで一通りメインの機能は確認できました。ではいよいよコード追加してプルリクを投げましょうというところですが、テストは?ということでユニットテストとカバレッジ状況をチェックしてみます。また自作アプリに対してのテスト方法もチェックしてみます。 Bottleのユニットテストとカバレッジ状況 GitHub上のBottleをクローンすると直下にtestディレクトリがあります。ここにずらーっとテストがありますが、よくよく見てみるとtestall.pyという全実行のエントリポイントが用意されているのでそれを実行します。 # noseを入れてない場合はインストール pip install nose # こんな感じに実行 nosetests –with-coverage –cover-html testall.py でました。bottle.pyだけを見てみると76%しっかりテストが書かれてるのがわかります。 bottle.py 2277 485 849 79 76% そしてhtmlをチェックコードのカバレッジ状況が一目瞭然。あとは足りないとこテストしたり自分で処理追加してテストしたり。これでいろいろ安心。 BottleでつくったWebアプリのテスト方法 せっかくなのでWebアプリのテスト方法も見てみます。これは公式ドキュメントにしっかり書かれているのでそこをみればだいたい分かると思います。noseインストール pip install nose テスト対象コード import bottle @bottle.route(‘/’) def index(): return ‘Hi!’ if __name__ == ‘__main__’: bottle.run() テストコード単純にindex()を実行して結果をassertする import mywebapp def test_webapp_index(): assert mywebapp.index() == ‘Hi!’ テスト実行 nosetests test_app.py Ran 1 test in 0.014s OK 上記は直接関数をテストする方法ですがwebtestを使ってwebベースでアクセスしてテストする方法もあります。ただこの方法をやってみたところ明示的 app = Bottle()を宣言しないとうまく実行できませんでした。ここら辺はもう少しwebtestの使い方がわかればなんとかなるかもしれません。webtestインストール pip install webtest テスト対象コード from bottle import Bottle # アプリを明示的に作成 app = Bottle() @app.route(‘/’) def index(): return ‘Hi!’ if __name__ == ‘__main__’: run(app=app) テストコード公式ドキュメントではログイン・ログアウトのテストが書かれてましたがここでは省略 from webtest import TestApp import mywebapp def test_index(): app = TestApp(mywebapp.app) assert …

Python Bottleのソースを読む ルータ編

2016/12/05   -Python
 ,

Pythonの軽量WebフレームワークBottleのソースを読む ルータ編 Class Hierarchy ここの部分 Bottleのルータについて bottleのルータは特に継承関係はなくRouteとRouterクラスで構成されている Routerの役割 A Router is an ordered collection of route->target pairs. It is used to efficiently match WSGI requests against a number of routes and return the first target that satisfies the request. The target may be anything, usually a string, ID or callable object. A route consists of a path-rule and a HTTP method.The path-rule is either a static path (e.g. `/contact`) or a dynamic path that contains wildcards (e.g. `/wiki/`). The wildcard syntax and details on the matching order are described in docs:`routing`. RouterはRoute情報のコレクションを保持する。 Routeの役割 This class wraps a route callback along …

Python Bottleのソースを読む プラグイン編

2016/12/05   -Python
 ,

Pythonの軽量WebフレームワークBottleのソースを読む プラグイン編Bottleを触ってみると通常のWebフレームワークには用意されているであろう機能がなかったりします。これはマイクロフレームワークであるが故であり、すべてがそろってない状態がむしろ正しい状態と言えます。Bottleではそういったものを補うためにプラグインが用意されていてある程度の機能はそちらでまかなうことができます。また、Plugin Development Guide を参考にしてプラグインを自作することも可能です。 Class Hierarchy plugin用クラスはなくインターフェースが定義されているのでそれにしたがって実装します。 プラグインの使い方 公式ドキュメントに簡単な使い方が乗っているのでこちらを参考にすれば簡単に導入することができます。以下がサンプルコードで、簡単な流れとしてはinstall()で任意のプラグインをインストールするリクエスト時にプラグイン実行となります。サンプルコードでは、プラグインでkwargsにdbをセットされている為ルートのアクションでdb変数が利用できるようになってます。 from bottle import route, install, template from bottle_sqlite import SQLitePlugin install(SQLitePlugin(dbfile=’/tmp/test.db’)) @route(‘/show/<post_id:int>’) def show(db, post_id): c = db.execute(‘SELECT title, content FROM posts WHERE id = ?’, (post_id,)) row = c.fetchone() return template(‘show_post’, title=row[‘title’], text=row[‘content’]) @route(‘/contact’) def contact_page(): ”’ This callback does not need a db connection. Because the ‘db’ keyword argument is missing, the sqlite plugin ignores this callback completely. ”’ return template(‘contact’) プラグインの作り方 プラグインの作り方も公式ドキュメントにもっともシンプルな形のサンプルがあります。これは実行速度をレスポンスヘッダーにつけて返す処理ですが、サーバ起動時にstopwatchをインストールし、リクエストが来た際にデコレータを実行(表現あってるか分からない)することでリクエストの処理時間は計測できるようになっています。 from bottle import response, install, route import time def stopwatch(callback): def wrapper(*args, **kwargs): start = time.time() body = callback(*args, **kwargs) end = time.time() response.headers[‘X-Exec-Time’] = …

Python Bottleのソースを読む リクエスト・レスポンス編

2016/12/05   -Python
 ,

Pythonの軽量WebフレームワークBottleのソースを読む リクエスト・レスポンス編   Class Hierarchy ここの部分 リクエスト受付からレスポンスまで 前回、サーバの立ち上げ時にルーティングが読み込まれるところまでを確認したので今回はリクエスト受付からレスポンスを返すまでを見てみる。 まずリクエストが来るとwsgiref/handlers.pyのrunが呼ばれ、Bottleの__call__が呼び出される wsgiref/handlers.py self.setup_environ() # リクエストを処理しレスポンスを取得する self.result = application(self.environ, self.start_response) # レスポンスを返す self.finish_response() bottle.py def __call__(self, environ, start_response): “”” Each instance of :class:’Bottle’ is a WSGI application. “”” return self.wsgi(environ, start_response) リクエストを処理する際に、LocalRequestのbindを呼び出しBaseRequestのコンストラクタにてenvironとLocalRequestをセットする。これでBaseRequestのラッパーからパラメータを取得可能になる。レスポンスも同様にLocalResponseのbindを呼び出す。 try: out = None environ[‘bottle.app’] = self request.bind(environ) response.bind() try: self.trigger_hook(‘before_request’) except HTTPResponse: return _e() # ここでリクエストの主処理を実行する out = _inner_handle() return out; finally: if isinstance(out, HTTPResponse): out.apply(response) self.trigger_hook(‘after_request’) BaseRequestのコンストラクタ self.environ = {} if environ is None else environ self.environ[‘bottle.request’] = self リクエスト情報は以下のように取得可能になる def index(): request.forms.get(‘test’) return ‘TOP’ LocalRequestは以下の通りマルチスレッドに対応している #: A thread-safe instance of :class:`LocalRequest`. If accessed from within a #: request …

Python Bottleのソースを読む テンプレート編

2016/12/05   -Python

前回に続きPythonの軽量WebフレームワークBottleのソースを読む テンプレート編 Class Hierarchy ここの部分 Bottleのテンプレートについて Bottleのテンプレートは、Simple、Cheetah、Jinja2、Makoの4種類があり、BaseTemplateを継承しているPythonのテンプレートの種類(参考)http://www.cmscom.jp/blog/af0ga8 テンプレートの使い方 テンプレートの拡張子には以下が利用できる extensions = [‘tpl’, ‘html’, ‘thtml’, ‘stpl’] 例えばjinja2を使う場合は以下のように呼び出すことができる(jinja2をpip installする必要あり) @route(‘/’) def jinja2(): name = ‘text’ return jinja2_template(‘jinja2’, name=name) view {{ name }} テンプレート呼び出しの処理をみてみる 各テンプレートは以下のように設定されている。 functools.partialにfunc templateが渡されている mako_template = functools.partial(template, template_adapter=MakoTemplate) cheetah_template = functools.partial(template, template_adapter=CheetahTemplate) jinja2_template = functools.partial(template, template_adapter=Jinja2Template) jinja2_templateが呼ばれた時の処理 *argsにはjinja2が、**kwargsにはtemplate_adapter=Jinja2Templateが渡される def template(*args, **kwargs): 引数からjinja2テンプレート名を取得 tpl = args[0] if args else None Jinja2Templateを取得 adapter = kwargs.pop(‘template_adapter’, SimpleTemplate) テンプレートパスをlookup(パスは./views/か./) lookup = kwargs.pop(‘template_lookup’, TEMPLATE_PATH) Jinja2Templateをインスタンス化 TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings) BaseTemplateのコンストラクタの最後のprepareでJinja2Templateのprepareが呼ばれる self.prepare(**self.settings) ここではじめてjinja2がimportされる from jinja2 import Environment, FunctionLoader テンプレートのrenderメソッドにてレンダリングしviewを返す return TEMPLATES[tplid].render(kwargs) BottleのテンプレートはBaseTemplateを継承しprepareとrenderを実装することで使える仕組みになっている def prepare(self, **options): “”” Run preparations (parsing, caching, …). It should be possible …