HTTPクライアントの単体テスト

HTTPクライアントとして書いたプログラムから、リクエストが正しく出ているか、またサーバから受け取ったデータを正しく処理できるか、テストコードとして書きたい場合があります。
もちろんlocalhostでHTTPサーバを立ち上げて結合テストを行うことも考えられますが、プロジェクトの進捗として必ずしもHTTPサーバ側のコードが先に完成しているとは限りませんし、HTTPクライアントだけで単体テスト可能になっていた方が嬉しいことが多いはずです。
ここでは偽のソケットを作ってHTTPクライアントが正しく動作しているか確認するコードを書いてみます。
PyUnitなどでテストを書く場合、httplib.HTTP._connection_classを書き換える部分はsetUp/tearDownメソッドに書いて、sendbuf/recvbufはテストメソッドごとに用意するのがいいかもしれません。

# -*- coding: utf-8 -*-
from StringIO import StringIO
import httplib
from urllib import URLopener

## 送受信内容をトラップする偽ソケット
class FakeSocket:
    sendbuf = None
    recvbuf = None
    def close(self):
        pass
    def sendall(self, data):
        self.sendbuf.write(data)
    def makefile(self, mode, bufsize):
        return self.recvbuf

## HTTP接続をFakeSocketに差し替える
class FakeHTTPConnection(httplib.HTTPConnection):
    def connect(self):
        self.sock = FakeSocket()
httplib.HTTP._connection_class = FakeHTTPConnection

## FakeSocketクラス変数にバッファをセット
FakeSocket.sendbuf = StringIO()
FakeSocket.recvbuf = StringIO('aaaaaaaaaaaaaaaaaaaaaa')

## urllibを使うが本当のHTTPリクエストは送信されない
u = URLopener()
f = u.open('http://server/path/file')

## recvbufの内容がリスポンスとして返されている
assert f.read() == 'aaaaaaaaaaaaaaaaaaaaaa'

## リクエスト内容はsendbufに保存されている
assert FakeSocket.sendbuf.getvalue() == 'GET /path/file HTTP/1.0\r\nHost: server\r\nUser-agent: Python-urllib/1.16\r\n\r\n'

## HTTP接続を通常のソケットに戻す
httplib.HTTP._connection_class = httplib.HTTPConnection