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

Python Developers Festa 2012.03に参加してきましたよ! #pyfes

PythonistaのイベントであるPython Developers Festa(以下pyfes)に参加してきました!

今回は前回のpyfesから半年が経ち、自分のPython力も少しは上がったせいか、Pythonを使っていて色々と疑問が出てきたところだったので、意気揚々と参加できました。

具体的にはPythonのテストに関するハンズオンに参加できてかなりの収穫があったと思います。

後半戦のプレゼンはネットワーク系の話しなので全く理解できず!
ただ、ZooKeeperとかCloudFoundryという名前だけでも知っておくと後々良いことがありそうな予感がしました。

今日やったテストハンズオンの復習・感想的なメモでもこれから書いていきます。

  • 講師は@さんと@さん。多くの質問にも丁寧に応対してくださり、非常に助かりました。
  • 当日使った教材はこちらにあります。まだ全部やれているわけではない…というか出来ていないところが多いので、個人的に要復習。
  • 上記リンクの課題にあるように、Python3用のコードを直すところから始まり、doctestの内容をunittestで書いてみるという課題、coverageを100%にする課題、toxを使って完全に隔離された環境のテストを実行する課題、などなどが課されました。
  • Python3用のコードだったのでPython3.xを入れてやろうかと思いましたが、環境構築が時間内に終わりそうに無かったので方針変更してコードを直す方向に。途中で講師の@さんがpython2.7に対応したコードをアップしてくれたので途中からやりやすかったです。
  • (Python3用のコードのためpython2.7で色々ひっかかったとはいえ)doctestがほぼ全ての関数に書かれていたため、doctestをunittestに変える課題は慣れてきたらスラスラできる(?)ようになりました。doctestイイネ!
  • というか、ちゃんと中身を読まずに(頭もあまり使わず)右から左に受け流すかのごとく、doctestのコードをunittestにしていたのは個人的に良かったのだろうか?とか思ったり思ったり。せっかくdoctestがあるんだから、もう少し中身を読みつつやればよかったかなと。
  • 途中詰まったところのメモ
    • doctestは空白の文字列が連続していても一つとしか認識しないが、nose*1は空白の文字列が複数連続していたらその数だけ空白文字列があるものとして認識するため、assertEqualで思わぬエラーが出たりする。(cf. apis.pyのformat_task関数内のdoctest)
    • Python3用のコードで手直ししたところ=覚えている範囲では「u'hogehoge'」とか「名前空間の違い(例えばConfigParser)」とか。

…何か適当に書いてしまった感が半端無いです、これ。

とにもかくにも、個人的に作っているプログラムでちょうどテストが必要になってきたところだったので、今回のテストハンズオンは渡りに船でした。もう少し時間があればMockライブラリを使ったテストダブルの作り方を勉強したかったですね。これは要自習ですね。

講師の@さん曰く、「Mockとかは慣れてない人にはそもそも概念が難しいよ」とのことなので、腰を据えて取り組まないといけないかもしれません。

前回に引き続き、今回も参加して良かったです。次回の参加の時までにもっとレベルアップしたいなあと思いました。

最後に、右から左に受け流すかのごとく書いた(まだ途中の)課題のunittestコードです。適当に貼り付けておきます。色々間違っていると思う*2ので、あくまで参加メモくらいの意識で見てもらえれば幸いです。

# -*- coding: utf-8 -*-

import unittest
from datetime import datetime
from datetime import time

import apis

STATE_NEW = 1
STATE_ACTIVE = 2
STATE_FINISHED = 3
state_str = {
        STATE_NEW: "新規",
        STATE_ACTIVE: "実行中",
        STATE_FINISHED: "完了",
        }

class AodagTasksTests(unittest.TestCase):

    def test_create_task(self):
        result = {'due_date': datetime(2012, 4, 1, 0, 0),
                  'state': 1,
                  'required_time': time(0, 25),
                  'name': 'たすく'}
        self.assertEqual(result,
            apis.create_task("たすく", datetime(2012, 4, 1), time(0, 25), 1))

    def test_add_task(self):
        result = [{'due_date': datetime(2012, 4, 1, 0, 0),
                  'state': 1,
                  'required_time': time(0, 25),
                  'name': 'たすく'}]
        t = apis.create_task("たすく", datetime(2012, 4, 1), time(0, 25), 1)
        tasks = []
        apis.add_task(tasks, t)
        self.assertEqual(result, tasks)

    def test_format_task(self):
        result = '新規 たすく: 2012-04-01まで 予定所要時間00時間25分'
        t = apis.create_task("たすく", datetime(2012, 4, 1), time(0, 25), 1)
        self.assertEqual(result, apis.format_task(t))

    def test_is_new_task(self):
        t = apis.create_task("たすく", datetime(2012, 4, 1), time(0, 25), 1)
        self.assertEqual(STATE_NEW, t.get('state'))

    def test_is_active_task(self):
        t = apis.create_task("たすく", datetime(2012, 4, 1), time(0, 25), 2)
        self.assertEqual(STATE_ACTIVE, t.get('state'))

    def test_is_finished_task(self):
        t = apis.create_task("たすく", datetime(2012, 4, 1), time(0, 25), 3)
        self.assertEqual(STATE_FINISHED, t.get('state'))

    def test_is_past(self):
        due_date = datetime(2012, 4, 30)
        t = apis.create_task("たすく", datetime(2012, 4, 29), time(0, 25), 1)
        self.assertTrue(apis.is_past(due_date, t))
        t = apis.create_task("たすく", datetime(2012, 4, 30), time(0, 25), 1)
        self.assertFalse(apis.is_past(due_date, t))
        t = apis.create_task("たすく", datetime(2012, 5, 1), time(0, 25), 1)
        self.assertFalse(apis.is_past(due_date, t))

    def test_filter_many(self):
        f1 = lambda t: t['name'] == 'タスク'
        f2 = lambda t: t['due_date'] == datetime(2012, 4, 1)
        t = apis.create_task("たすく", datetime(2012, 4, 1), time(0, 25), 1)
        g = apis.filter_many([f1, f2], t)

*1:unittestが、かも

*2:最後のtest_filter_many関数なんて、中にテストが書いてない!