6

I'm trying to unit test python code which accesses a remote service. I'm using PyUnit with python 2.7.

In the setUpClass method, the code prompts the user to enter the password for the service. I want to keep everything modular, so I've created separate unit test classes for each class being tested. These classes all access the same remote service, and they all use a single definition of the setUpClass method fom a super class.

My problem is that I have to re-enter the password multiple times (once for every test class). I'm lazy. I only want to enter the password once for all unit tests. I could avoid the issue by hard-coding the password in the unit test, but that's a terrible idea. The other option is to shove everything into one massive class derived from unittest.TestCase, but I want to avoid that route because I like modularization.

Here's how the code is structured:

import unittest
from getpass import getpass

class TestCommon(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        pwd = getpass()

class test_A(TestCommon):
    # ...individual unit tests for class A

class test_B(TestCommon):
    # ...individual unit tests for class B

In this example, I would have to enter the password twice: once for class A and once for class B.

Anyone have advice on a secure way for me to do a one-time password entry right at the beginning of the unit test run? Thanks!

RobotNerd
  • 2,608
  • 3
  • 29
  • 28
  • Related: [Writing a Python functional test which requires a username and password](https://stackoverflow.com/q/47143368/3357935) – Stevoisiak Mar 16 '18 at 13:24
  • 1
    By accessing a remote service from your unit tests you actually test that service along with your classes. Why don't you mock the remote service away? – otterrisk Aug 27 '19 at 12:48

3 Answers3

4

Class definition is executed once.

import unittest
from getpass import getpass

class TestCommon(unittest.TestCase):
    pwd = getpass()

class test_A(TestCommon):
    def test_a(self):
        self.assertEqual(self.pwd, 'secret')

class test_B(TestCommon):
    def test_b(self):
        reversed_pwd = self.pwd[::-1]
        self.assertEqual(reversed_pwd, 'terces')

The password is accessible via self.pwd or TestCommon.pwd.

falsetru
  • 357,413
  • 63
  • 732
  • 636
  • When I run the command `python -m unittest discover` I get prompted twice for the password. Is it because I define setUpClass() in the superclass? Should I rewrite it like your sample code? – RobotNerd Jun 29 '13 at 03:43
  • @RobotNerd, How do you access the password in subclasses? – falsetru Jun 29 '13 at 03:44
  • @RobotNerd, If you access the password in subclasses as `self.pwd` or `TestCommon.pwd`, only rewrite TestCommon part. – falsetru Jun 29 '13 at 03:47
0

In your setUpClass() function, you can save the password as a property of self, then make each test a part of your TestCommon class.

import unittest
from getpass import getpass

class TestCommon(unittest.TestCase):
    @classmethod
    def setUpClass(self):
        self.pwd = getpass()

    def test_A(self):
        self.assertEqual(self.pwd, 'TEST A')

    def test_B(self):
        self.assertEqual(self.pwd, 'TEST B')
Stevoisiak
  • 23,794
  • 27
  • 122
  • 225
  • I think `setUpClass()` is called for every subclass of `TestCommon`, so this would still force me to enter the password multiple times. – RobotNerd Nov 07 '17 at 01:36
0

If you want to fully automate your tests, I recommend storing your password as an environment variable.

import os
password = os.environ["password"]

class test_A(self):
    self.assertEqual(password, "secret")

class test_B(self):
    self.assertNotEqual(password, "not_a_secret")

Environment variables are dynamic values which are specific to your runtime environment. This allows you to load private variables when needed, without having to save the data in your code.

These variables are environment-specific, meaning they will not be shared with other users. If someone else tries to run your test in a different environment, they will need to set their own password.

Most Python development environments have their own methods for setting environment variables (PyCharm, Visual Studio, Eclipse/PyDev). However, you can temporarily set one for your environment with the following:

import os
os.environ["password"] = "my_secret_password"

See also:

Stevoisiak
  • 23,794
  • 27
  • 122
  • 225