質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.64%

自作モジュールのテストコードがImportErrorで動きません

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 4,200

manzyun

score 2177

現在「投稿に期限がついており、期限になったら削除されるBBS」を以下で作っております。

  • Ubuntu Wily
  • Python3.5
  • Flask
  • MongoDB

リポジトリは以下に公開しております。
manzyun / Setsuna — Bitbucket

ディレクトリ構成に関しては以下になります。詳しくは上記リポジトリをご覧いただけると幸いです。

  • pyvenvディレクトリ
      - アプリケーションコード
        - テストコード

しかし、この構成でリポジトリのコードだと、unittestやnose2を動かすと
ImportError: No module named 'conf'
とでてしまい、テストも実行されません。また、pudbでテストコードを動かしても
ImportError: No module named 'setsuna'
となってしまい、テストが全く実行出来ずに困っています。

Pythonのモジュールインポートの話だと思うのですが、
もしかしたらconf.pyの中身が変数しか無いことが原因なのかもしれないのかと思っているのですが、僕自身現状がどうなっているのかわかりません。

参考までに、該当するテストコードとテスト対象のコードを貼り付けます。

import unittest

from setsuna import conf, models
from unittest import TestCase, expectedFailure
from pymongo import MongoClient
import json
import datetime
import calendar


testdata = {"content": "美味しい美味しいスープカレー",
            "limit": calendar.timegm(
                datetime.datetime.utcnow().timetuple()),
            "delkey": "hogefuga"}

class TestPost(TestCase):
    client = MongoClient(conf._conf["address"], conf._conf["port"])
    db = client[conf._conf["database"]]
    collection = db[conf._conf["collection"]]

    def setUp(self):
        # とりあえず書く
        self.testindex = TestPost.collection.insert_one(testdata)

    def tearDown(self):
        TestPost.collection.delete_one(self.testindex)

    def test_make_model(self):
        """ モデルが読み込まれてインスタンスが生成されるか """
        model = models.Post()
        model.read(self.testindex)

        d = {"content": model.content,
                "limit": model.limit,
                "delkey": model.delkey}

        del testdata['_id']

        self.assertDictEqual(d, testdata)

    def test_insert_model(self):
        """ 生成したインスタンスが、インスタンスの情報を維持したまま挿入されるか """
        model = models.Post()
        model.content = "にくまん、あんまん、カレーまん"
        model.delkey = "nununeno"
        sample_id = model.post()

        model_sample = models.Post.read(sample_id)

        self.assertDictEqual(model, model_sample)


    def test_delete_model(self):
        """ 狙ったレコードがパスワードが合致した場合に削除されるか """
        model = models.Post(self.testindex)
        model.delete(testdata["delkey"])

        self.assertRaises(NoneRecordException,
                self.collection.find_one({"unique_id": testdata["unique_id"]}))

    def test_dead_model(self):
        """ リミットオーバーした場合投稿が削除されるか """
        model = models.Post(self.testindex)
        model.apoptosis()

        self.assertRaises(NoneRecordException,
            self.collection.find_one({"unique_id": testdata["unique_id"]}))

if __name__ == "__main__":
    unittest.main()
from conf import _conf
from pymongo import MongoClient
import json
import datetime
import random
import bson


class Post():
    # DB Connection
    connect = MongoClient(_conf["address"], _conf["port"])
    posts = connect[_conf["database"]][_conf["collection"]]

    def __init__(self):
        # Read DB
        self._id = None
        self.content = ""
        self.limit = 0.0
        self.delkey = ""

    def read(self, _id):
        # Read DB
        self._id = _id
        post = Post.posts.find_one({"_id": self._id})
        self.content = post["content"]
        self.limit = post["limit"]
        self.delkey = post["delkey"]

    def post(self, content="", delkey=""):
        self.content = content
        self.delkey = delkey
        if self.delkey == "":
            self.delkey = make_delkey()
        self.limit = datetime.timedelta(seconds=28800)

        # Writing DB
        try:
            result = Post.posts.insert_one({"content": self.content,
                "limit": self.limit,
                "delkey": self.delkey})
            return str(result["_id"])
        except Exception as e:
            return e

    def delete(self, delkey):
        try:
            if self._id == _id and self.delkey == delkey:
                # Remove post in DB
                collection.delete_one({"_id": self._id})
                return True
            else:
                return False
        except Exception as e:
            return e

def make_delkey(length=6):
    # Make font map
    alphabets = []
    codes = (('a', 'z'), ('A', 'Z'), ('0', '9'))
    for r in codes:
        chars = map(chr, range(ord(r[0]), ord(r[1]) + 1))
        alphabets.extend(chars)

        password = [random.choice(alphabets) for _ in range(length)]
        delkey = "".join(password)

        return delkey

if __name__ == "__main__":
    test = Post()

    test.connect = "にくまん"
    test.delkey = "hogefuga"
    test.limit = 123456.78

    print(test.post())

以上です。長文失礼いたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

check解決した方法

+2

 2016/3/7 午前中

すみません、自己解決(?)しました。

Pythonで自作モジュールを手軽に使う - Qiita

ただ、この解法、個人的にはクールじゃないなあと思っているのですが、僕の能力ではこれ以上考えても埒が明かないので、この方法でテストを継続しようと思います。

 2016/3/7 15:33 追記

各モジュールにsys.path.appendを入れるのはやっぱり面倒なので、以下モジュールを作ってテストディレクトリ以下に入れて使うのが良さそうですね。
import from parent directory | Python Adventures

 2016/3/7 18:21 追記

拙いながらこんなモジュールを書きました。
コレをテストコードのディレクトリに置いてimport_my_moduleメソッドでPYTHONPATHにパスを追加することで、理想通りテストが動くようになりました。
This file deploy your script directory. Then your make module import easly maybe.

また、confのエラーに関しては相対インポートを使ってインポートさせるようにして動作させました。
6. モジュール (module) — Python 3.5.1 ドキュメント

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る