2014-04-16 84 views
5

我想添加測試到我的基本應用程序。訪問所有內容需要登錄。(unittest)測試瓶安全:無法通過登錄頁

這裏是我的測試用例類:

class MyAppTestCase(FlaskTestCaseMixin): 

    def _create_app(self): 
     raise NotImplementedError 

    def _create_fixtures(self): 
     self.user = EmployeeFactory() 

    def setUp(self): 
     super(MyAppTestCase, self).setUp() 
     self.app = self._create_app() 
     self.client = self.app.test_client() 
     self.app_context = self.app.app_context() 
     self.app_context.push() 
     db.create_all() 
     self._create_fixtures() 
     self._create_csrf_token() 

    def tearDown(self): 
     super(MyAppTestCase, self).tearDown() 
     db.drop_all() 
     self.app_context.pop() 

    def _post(self, route, data=None, content_type=None, follow_redirects=True, headers=None): 
     content_type = content_type or 'application/x-www-form-urlencoded' 
     return self.client.post(route, data=data, follow_redirects=follow_redirects, content_type=content_type, headers=headers) 

    def _login(self, email=None, password=None): 
     email = email or self.user.email 
     password = password or 'password' 
     data = { 
      'email': email, 
      'password': password, 
      'remember': 'y' 
      } 
     return self._post('/login', data=data) 


class MyFrontendTestCase(MyAppTestCase): 

    def _create_app(self): 
     return create_app(settings) 

    def setUp(self): 
     super(MyFrontendTestCase, self).setUp() 
     self._login() 

我正在我的測試使用nosetests在終端像這樣:source my_env/bin/activate && nosetests --exe

這樣的基本測試失敗:

class CoolTestCase(MyFrontendTestCase): 

    def test_logged_in(self): 
     r = self._login() 
     self.assertIn('MyAppName', r.data) 

    def test_authenticated_access(self): 
     r = self.get('/myroute/') 
     self.assertIn('MyAppName', r.data) 

從輸出,我發現r.data只是登錄頁面的HTML,沒有任何錯誤(例如,錯誤的用戶名或密碼)或警報(「請登錄以訪問此內容頁」)。

我在setUp過程登錄,所以test_authenticated_access應該有讓我無論是訪問/myroute/或重定向我與閃過消息登錄頁面「請登錄訪問此頁面」。但事實並非如此。

我無法弄清楚什麼是錯的。我根據我的測試過的人的我瓶文檔中發現this app boilerplate

+0

我看你使用CSRF令牌 - 你嘗試禁用它們,看是否得到您更遠?我知道他們已經比我記憶中的更多時間讓我受到了考驗。 –

+0

如果您使用(或可以使用)Flask 0.10,我強烈推薦使用新的假上下文:http://flask.pocoo.org/docs/testing/#faking-resources-and-context我們是目前正在做一些與您爲我們的測試所做的非常相似的事情,只要我們可以升級,我就會全力以赴支持這一點。 –

+0

@RachelSanders - 我已經把'WTF_CSRF_ENABLED'設置爲'False' –

回答

2

我終於想通了殺害自己一個星期後...

有幾個問題:

  1. 有是Flask-Security和Flask-SSLify之間的一些衝突。雖然自動https重定向在實際的Web服務器上正常工作,但在測試中它會阻止完全登錄。我通過製作假測試路線並嘗試使用隨機數據來解決這個問題。測試客戶端中沒有POST工作。要解決這個問題,我必須將我的測試配置DEBUG更改爲True。請注意,這是不是一個偉大的解決辦法,因爲這樣的東西CSS和JS不會被編譯和其他應用程序的行爲可能會有所不同......

  2. 瓶,安全性不factory_boy工作(或我沒有使用factory_boy正確嗎?)。我試圖通過factory_boy創建用戶和角色,因爲我看到一些使用它的示例應用程序教程。在我解決了上述問題後,它一直告訴我用戶不存在。我最終從Flask-Security自己的測試中竊取了代碼,用於創建具有不同角色的假用戶。

有問題的代碼:

session = db.create_scoped_session() 

class RoleFactory(SQLAlchemyModelFactory): 
    FACTORY_FOR = Role 
    FACTORY_SESSION = session 

    id = Sequence(int) 
    name = 'admin' 
    description = 'Administrator' 


class EmployeeFactory(SQLAlchemyModelFactory): 
    FACTORY_FOR = Employee 
    FACTORY_SESSION = session 

    id = Sequence(int) 
    email = Sequence(lambda n: 'user{0}@app.com'.format(n)) 
    password = LazyAttribute(lambda a: encrypt_password('password')) 
    username = Sequence(lambda n: 'user{0}'.format(n)) 
    #last_login_at = datetime.utcnow() 
    #current_login_at = datetime.utcnow() 
    last_login_ip = '127.0.0.1' 
    current_login_ip = '127.0.0.1' 
    login_count = 1 
    roles = LazyAttribute(lambda _: [RoleFactory()]) 
    active = True 

這是我的測試案例:

class MyAppTestCase(FlaskTestCaseMixin, MyTestCase): 

    def _create_app(self): 
     raise NotImplementedError 

    def _create_fixtures(self): 
     #self.user = EmployeeFactory() 
     populate_data(1) 

    def setUp(self): 
     super(MyAppTestCase, self).setUp() 
     self.app = self._create_app() 
     self.client = self.app.test_client() 
     self.app_context = self.app.app_context() 
     self.app_context.push() 
     db.create_all() 
     self._create_fixtures() 
     self._create_csrf_token() 

    def tearDown(self): 
     super(MyAppTestCase, self).tearDown() 
     db.drop_all() 
     self.app_context.pop() 

    def _post(self, route, data=None, content_type=None, follow_redirects=True, headers=None): 
     content_type = content_type or 'application/x-www-form-urlencoded' 
     return self.client.post(route, data=data, follow_redirects=follow_redirects, content_type=content_type, headers=headers) 

    def _login(self, email=None, password=None): 
     email = email or '[email protected]' #self.user.email 
     password = password or 'password' 
     data = { 
      'email': email, 
      'password': password, 
      'remember': 'y' 
      } 
     return self._post('/login', data=data) 
+0

你列出了問題測試,但不是工作測試。在這裏找不到它。 https://github.com/mattupstate/flask-security – nycynik

1

我曾與登錄/註銷的問題與燒瓶安全(測試使用Flask-登錄)應用程序也。我通過嘲笑flask_login._get_user函數並將app.login_manager._login_disabled設置爲True來解決此問題。

嘲笑用戶

情景管理:

class LoginLogoutMixin(object): 
    @contextmanager 
    def login(self, user): 
     mock_get_user = patch('flask_login._get_user', Mock(return_value=user)) 
     self.app.login_manager._login_disabled = True 
     mock_get_user.start() 
     yield 
     mock_get_user.stop() 
     self.app.login_manager._login_disabled = False 

而且它的一個例子是使用:

class ProtectedViewTestCase(LoginLogoutMixin, TestCase): 

    def setUp(self): 
     super(ProtectedViewTestCase, self).setUp() 
     # create user here 

    def test_protected_view(self): 
     with self.login(self.user): 
      rv = self.client.get(url_for('protected_view')) 
      self.assert200(rv)