Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python unittest setting a global variable correctly

I have a simple method that sets a global variable to either True or False depending on the method parameter.

This global variable is called feedback and has a default value of False.

When I call setFeedback('y') the global variable will be changed to be feedback = True. When I call setFeedback('n') the global variable will be changed to be feedback = False.

Now I am trying to test this using unittest in Python:

class TestMain(unittest.TestCase):

    def test_setFeedback(self):

        self.assertFalse(feedback)
        setFeedback('y')
        self.assertTrue(feedback)

When I run this test I get the following error: AssertionError: False is not true.

Since I know that the method works correctly, I assume that the global variables are reset somehow. However, since I am still very new to the Python environment, I don't know exactly what I am doing wrong.

I have already read an article here about mocking, but since my method changes a global variable, I don't know if mocking can solve this.

I would be grateful for suggestions.

Here is the code:

main.py:

#IMPORTS
from colorama import init, Fore, Back, Style
from typing import List, Tuple

#GLOBAL VARIABLE
feedback = False

#SET FEEDBACK METHOD
def setFeedback(feedbackInput):
    """This methods sets the feedback variable according to the given parameter.
       Feedback can be either enabled or disabled.

    Arguments:
        feedbackInput {str} -- The feedback input from the user. Values = {'y', 'n'}
    """

    #* ACCESS TO GLOBAL VARIABLES
    global feedback

    #* SET FEEDBACK VALUE
    # Set global variable according to the input
    if(feedbackInput == 'y'):

        feedback = True
        print("\nFeedback:" + Fore.GREEN + " ENABLED\n" + Style.RESET_ALL)
        input("Press any key to continue...")

        # Clear the console
        clearConsole()

    else:
        print("\nFeedback:" + Fore.GREEN + " DISABLED\n" + Style.RESET_ALL)
        input("Press any key to continue...")

        # Clear the console
        clearConsole()

test_main.py:

import unittest
from main import *

class TestMain(unittest.TestCase):

    def test_setFeedback(self):

        self.assertFalse(feedback)
        setFeedback('y')
        self.assertTrue(feedback)


if __name__ == '__main__':
    unittest.main()
like image 346
CWhite Avatar asked May 01 '20 09:05

CWhite


People also ask

Which is the proper way of declaring a variable to avoid lint error?

Just add # NOQA at the end of the import line that is causing the error. This tells the linter to skip that particular line.

Should you avoid using global variables in Python?

While in many or most other programming languages variables are treated as global if not declared otherwise, Python deals with variables the other way around. They are local, if not otherwise declared. The driving reason behind this approach is that global variables are generally bad practice and should be avoided.


2 Answers

There are two problems with your test.

First, you use input in your feedback function, that will stall the test until you enter a key. You probably should mock input. Also you may consider that the call to input does not belong in setFeedback (see comment by @chepner).

Second, from main import * will not work here (apart from being bad style), because this way you create a copy of your global variable in the test module - changes in the variable itself will not be propagated to the copy. You should instead import the module, so that you will access the variable in the module.

Third (this is taken from the answer by @chepner, I had missed that), you have to make sure that the variable is at a known state at test begin.

Here is what should work:

import unittest
from unittest import mock

import main  # importing the module lets you access the original global variable


class TestMain(unittest.TestCase):

    def setUp(self):
        main.feedback = False  # make sure the state is defined at test start

    @mock.patch('main.input')  # patch input to run the test w/o user interaction
    def test_setFeedback(self, mock_input):
        self.assertFalse(main.feedback)
        main.setFeedback('y')
        self.assertTrue(main.feedback)
like image 125
MrBean Bremen Avatar answered Oct 19 '22 19:10

MrBean Bremen


You don't need to mock anything; you just need to ensure that the global variable is in a known state before running each test. Also, using from main import * creates a new global named feedback in your test module, distinct from main.feedback which setFeedback is modifying.

import main

class TestMain(unittest.TestCase):

    def setUp(self):
        main.feedback = False

    def test_setFeedback(self):

        self.assertFalse(feedback)
        main.setFeedback('y')
        self.assertTrue(feedback)
like image 37
chepner Avatar answered Oct 19 '22 21:10

chepner