SFTPのモックサーバをたてユニットテストをする
SFTP サーバを介してデータをやりとりするシステムのユニットテストを書くかもしれないので、どのように組むのか試してみました。
ひとまず、 Django で試します。
やること
SFTP サーバからファイルをとってくる関数を作成し、そのユニットテストを書きます。
ディレクトリ構成
- ディレクトリ直下で virtualenv 作ってます。
- settings にてこのディレクトリの . の位置を PRJ_ROOT としています。
- test_rsa.key と test_rsa.key.pub は秘密鍵と公開鍵です。
- test.txt はテストで利用します。
- var は (今回は利用しませんが) SQLite3 データ保存用に利用します。
$ tree . ├── app │   ├── __init__.py │   ├── manage.py │   ├── settings.py │   ├── sftp │   │   ├── __init__.py │   │   ├── models.py │   │   ├── tasks.py │   │   ├── tests.py │   │   └── views.py │   ├── urls.py │   └── wsgi.py ├── misc │   ├── test_rsa.key │   ├── test_rsa.key.pub │   └── test.txt ├── README.rst ├── setup.cfg ├── setup.py └── var
コード
次のような tasks.py を作成します。
# -*- coding: utf-8 -*- import os import paramiko from django.conf import settings def get_remote_file(filename, local_dir): """ SFTP サーバから filename ファイルを取得し、 dst_dir に配置する。 """ pkey = paramiko.RSAKey.from_private_key_file( '{0}'.format(settings.PRIV_KEY)) transport = paramiko.Transport(('localhost', 3373)) transport.connect(password='', pkey=pkey) sftp = paramiko.SFTPClient.from_transport(transport) sftp.get( '{0}'.format(os.path.join(os.curdir, os.sep, filename)), '{0}'.format(os.path.join(local_dir, filename))) sftp.close()
テストコード
次のような tests.py を作成します。
# -*- coding: utf-8 -*- import os import hashlib from time import sleep from subprocess import Popen from tempfile import mkdtemp from shutil import ( copyfile, rmtree, ) from django.conf import settings from django.test import TestCase from app.sftp.tasks import get_remote_file class SftpTest(TestCase): def setUp(self): """テストのための準備を整える 1. テスト用フォルダを作成する。 2. テストファイルを配置する。 3. サーバのルートに移動する。 4. 作成済みの秘密鍵を利用して、 SFTP サーバを起動する。 5. ローカルの作業ディレクトリの作成 """ self.sftproot = mkdtemp() self.testfile = 'test.txt' copyfile( os.path.join(settings.PRJ_ROOT, 'misc', self.testfile), os.path.join(self.sftproot, self.testfile)) self.curdir = os.getcwd() os.chdir(self.sftproot) self.proc = Popen([ '{0}'.format(os.path.join(settings.PRJ_ROOT, 'bin', 'sftpserver')), '-k', '{0}'.format(os.path.join(settings.PRJ_ROOT, 'misc', 'test_rsa.key')), '-l', 'WARNING', ]) # 起動に多少時間がかかるので、1秒待つ。 sleep(1) self.localdir = mkdtemp() def tearDown(self): """テストの後始末をする 1. テスト時の作業ディレクトリに移動する。 2. SFTP サーバを停止する。 3. テスト用フォルダを削除する。 4. 作業用フォルダを削除する。 """ os.chdir(self.curdir) self.proc.terminate() rmtree(self.sftproot) rmtree(self.localdir) def test_basic_addition(self): """取得したファイルのハッシュが期待したものであること""" get_remote_file('test.txt', self.localdir) with open(os.path.join(self.localdir, 'test.txt')) as fp: md5alg = hashlib.md5() md5alg.update(fp.read()) md5 = md5alg.hexdigest() # ファイルの中身は hoge のみ self.assertEqual(md5, 'c59548c3c576228486a1f0037eb16a1b')
まとめ
上記でテストが出きるようになると思います。ただ、 sftpserver の最終更新日が 2011/12/12 なのはちょっと古いのかもしれません、そこは注意願います。
参考
割と検索ったー
- paramiko paramiko の APIリファレンス
- sftpserver 0.2 : Python Package Index sftpserver の使い方
- @IT:Linuxでsshの鍵を作成するには ssh-keygen の使い方、忘れやすい
- はじめての Django アプリ作成、その 1 ― Django 1.4 documentation django-admin.py manage.py の使い方、あまり使わないので覚えてない
- 10.6. tempfile ― 一時的なファイルやディレクトリの生成 ― Python 2.7ja1 documentation mkdtemp() の使い方、一時フォルダの作成をするのでテスト時はとても便利
- 10.10. shutil ― 高レベルなファイル操作 ― Python 2.7ja1 documentation copyfile と rmtree の使い方、rmtree は便利
- 17.1. subprocess ― サブプロセス管理 ― Python 2.7ja1 documentation subprocess の使い方、コマンド利用時に参考に
- How do I "cd" in python - Stack Overflow 作業ディレクトリの移動の方法
- 15.1. os ― 雑多なオペレーティングシステムインタフェース ― Python 2.7ja1 documentation os.curdir、os.pardir、os.sep等
- 14.1. hashlib ― セキュアハッシュおよびメッセージダイジェスト ― Python 2.7ja1 documentation md5とかの使い方
- 5. 組み込み型 ― Python 2.7ja1 documentation read()は引数を与えないと全部読み込む
- Django1.4のプロジェクト構成案 - 今川館 Django のディレクトリ構成