[GO] Python debug and test module

Debugging and unit testing

assert statement

Raises an AssertionError exception if the variable test is false.

test = 0
data = "assertion error"

try:
    assert test,data
except AssertionError:
    print data
finally:
    print "the end"

If it is only the assert statement part, you can substitute it by writing as follows.

if __debug__:
  if not test
    raise AssertionError, data

Exception separation processing and content display

somedata = 1

#Tuple the exceptions you want to catch.
fatal_exceptions = (KeyboardInterrupt, MemoryError)

try:
    assert somedata
except fatal_exceptions, inst:  #You can receive the contents of the exception with the argument inst.
    print type(inst)    #Show exception type
    print inst          #Show the contents of the exception
    raise
#Catch all other exceptions
except Exception, inst:
    print type(inst)    #Show exception type
    print inst          #Show the contents of the exception
finally:
    print "the end"

UnitTest Slightly modified and added examples from the official tutorial.

import random
import unittest

class TestSequenceFunctions(unittest.TestCase):

    #Initialization process called every time
    #In addition to this, there is a method called after executing the test etc.
    def setUp(self):
        self.seq = range(10)

    #Describe the method name starting with test.
    def test_shuffle(self):
        random.shuffle(self.seq)
        self.seq.sort()

        #Check that the two arguments are equal.
        #Checking for inequality is assertNotEqual()You can do it with.
        self.assertEqual(self.seq, range(10))

        #Check that an exception will occur.
        # assertRaises(exception, callable, *args, **kwds)
        #Pass args and kwds to the function of the second argument and check that the exception specified by the first argument occurs.
        self.assertRaises(TypeError, random.shuffle, (1,2,3))

    def test_choice(self):
        element = random.choice(self.seq)

        #Check that the value of the argument is True.
        # bool(element)Equivalent to is True
        self.assertTrue(element in self.seq)

    def test_sample(self):

        #If only the exception argument is passed, the context manager is returned.
        #You can write the code to be tested inline.
        with self.assertRaises(ValueError):
            random.sample(self.seq, 20)
        for element in random.sample(self.seq, 5):
            self.assertTrue(element in self.seq)

if __name__ == '__main__':
    
    # main()Can be executed with.
    unittest.main()

    #Get tests to run individually.
    _test_choice = TestSequenceFunctions('test_choice')
    _test_sample = TestSequenceFunctions('test_sample')

    #It can be registered in the test suite and executed collectively by the runner.
    TestSuite = unittest.TestSuite()
    TestSuite.addTest(_test_choice)
    TestSuite.addTest(_test_sample)
    runner = unittest.TextTestRunner()
    runner.run(TestSuite)

    #You can get test functions collectively with the loader.
    suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
    unittest.TextTestRunner(verbosity=2).run(suite)

traceback The stack is the memory area where the program is currently running. A stack trace is a record of the execution status (function name, calling function name, line, statement, file name) of the program in the memory area, and this is an object in a script language such as Python. It is provided.

First, stack trace information can be obtained from sys.exc_info ().

exc_type, exc_value, exc_traceback = sys.exc_info()

Using these three variables as arguments, traceback.print_tb () and traceback.print_exception () display the information.

However, if you use traceback.print_exc () below, you can omit the variable acquisition from sys.exc_info (), and you can also display the type and contents, which is convenient. Basically you can use this.

import sys
import traceback

def somework():
    try:
        print a #Use undefined variables
    except Exception:
        print "error"
        traceback.print_exc(file=sys.stdout)
    finally:
        print "the end"

if __name__ == '__main__':
    somework()

Output result The module name, line, function name, cause statement, and cause error message are output.

error
Traceback (most recent call last):
  File "/Users/test.py", line 8, in somework
    print a
NameError: global name 'a' is not defined
the end

This is the output when traceback is not written. Nothing is output, just passing the exception.

error
the end

Next, a method of acquiring stack trace information as a list including tuples is shown. Here is also an example of getting a variable from sys.exc_info (). (Because it is used as an argument)

import sys
import traceback

def somework():
    try:
        print a #Use undefined variables
    except Exception:
        print "error"
        exc_type, exc_value, exc_traceback = sys.exc_info()
        print traceback.extract_tb(exc_traceback) #Get only current information
        print traceback.extract_stack() #Get a tuple containing the caller's function information

        #Call this if you just want to receive it as a list instead of a tuple and make it easier to display.
        traceback.format_tb(exc_traceback)
        traceback.format_stack()

        print "the end"

if __name__ == '__main__':
    somework()

nosetest

I referred to "Python Professional Programming".

installation of nose

pip install nose

Class to be tested This class simply adds or subtracts numbers to your bank account.

# coding: UTF-8

class NotEnoughFundsException(Exception):
    pass

class BankAccount(object):

    def __init__(self):
        self._balance = 0

    def deposit(self, amount):
        self.balance += amount

    def withdraw(self, amount):
        self.balance -= amount

    def get_balance(self):
        return self._balance

    def set_balance(self, value):
        if value < 0:
            raise NotEnoughFundsException

        self._balance = value

    #Prepare a wrapper so that you can use getters and setters.
    #to balance=Or+These functions will be called automatically by accessing with.
    balance = property(get_balance, set_balance)

Write code to test with nose. Preparing a fixed value for testing, such as 100 or 0, is called a data fixture.

# coding: UTF-8

import unittest

class BankAccountTest(unittest.TestCase):

    def _getTarget(self):
        from bankaccount import BankAccount
        return BankAccount

    #Create an instance to be tested.
    def _makeOne(self, *args, **kwargs):
        return self._getTarget()(*args, **kwargs)

    def test_construct(self):
        target = self._makeOne()
        self.assertEqual(target._balance, 0)

    def test_deposit(self):
        target = self._makeOne()
        target.deposit(100)
        self.assertEqual(target._balance, 100)

    def test_withdraw(self):
        target = self._makeOne()
        target._balance = 100
        target.withdraw(20)
        self.assertEqual(target._balance, 80)

    def test_get_blance(self):
        target = self._makeOne()
        target._balance = 500
        self.assertEqual(target.get_balance(), 500)

    def test_set_balance_not_enough_funds(self):
        target = self._makeOne()
        from bankaccount import NotEnoughFundsException
        try:
            target.set_balance(-1)
            self.fail()
        except NotEnoughFundsException:
            pass

Place the above two in the same directory and execute nose in that directory.

nosetest

.....
----------------------------------------------------------------------
Ran 5 tests in 0.004s

OK

Get coverage

pip install coverage
# nosetests -v --with-coverage
test_construct (test_bankaccount.BankAccountTest) ... ok
test_deposit (test_bankaccount.BankAccountTest) ... ok
test_get_blance (test_bankaccount.BankAccountTest) ... ok
test_set_balance_not_enough_funds (test_bankaccount.BankAccountTest) ... ok
test_withdraw (test_bankaccount.BankAccountTest) ... ok

Name          Stmts   Miss  Cover   Missing
-------------------------------------------
bankaccount      16      0   100%
----------------------------------------------------------------------
Ran 5 tests in 0.006s

OK

Easy-to-read coverage is output.

Output in XUNIT format with the "--with-xunit" option.

# nosetests -v -w . --with-coverage --with-xunit
test_construct (test_bankaccount.BankAccountTest) ... ok
test_deposit (test_bankaccount.BankAccountTest) ... ok
test_get_blance (test_bankaccount.BankAccountTest) ... ok
test_set_balance_not_enough_funds (test_bankaccount.BankAccountTest) ... ok
test_withdraw (test_bankaccount.BankAccountTest) ... ok

----------------------------------------------------------------------
XML: nosetests.xml
Name          Stmts   Miss  Cover   Missing
-------------------------------------------
bankaccount      16      0   100%
----------------------------------------------------------------------
Ran 5 tests in 0.006s

OK

Output in XML so that it can be read by jenkins.

coverage xml

mock

Normal usage

Class to be tested testモジュールのTest.py

# coding: UTF-8

class Widget(object):

    def __init__(self):
        self.value = 10

    def Additional(self, add):
        self.value += add
        return self.value

Test code

from mock import Mock
from test import Test

def mock_value(value):
    return 20 + value

if __name__ == '__main__':

    #Replace the function with a mock that returns 100 fixedly.
    Test.Widget.Additional = Mock(return_value=100)

    w = Test.Widget()
    print w.Additional(1)

    #If you want to do some calculation instead of a fixed value, side_Pass a dummy function in effect.
    Test.Widget.Additional = Mock(side_effect=mock_value)
    print w.Additional(1)
100
21

Replace class with mock

from mock import Mock
from test import Test

def mock_value(value):
    return 20 + value

if __name__ == '__main__':

    #Replace the class itself.
    Test.Widget = Mock()
    #The value returned by the function is fixed.
    Test.Widget.return_value.Additional.return_value = 10

    w = Test.Widget()
    print w.Additional(1)

    #Swap functions
    Test.Widget.return_value.Additional = mock_value
    print w.Additional(1)

patch Replaces a mock object running a function with a real object.

# coding: UTF-8

from mock import patch

#Specify the method and return value to replace only while this function is executing
@patch("Test.Widget.Additional", return_value=10)
def test_func(m): 

    import Test
    w = Test.Widget()
    print w.Additional(1)
    assert w.Additional(1) == 10

    #By the way, m received as an argument is an additional function replaced with a mock.
    #So here too the result is 10.
    #This time I replaced the method, but in the case of a class, it becomes a class.
    print m()

if __name__ == '__main__':
    test_func()

In the case of context manager format using with

# coding: UTF-8

from mock import patch

def test_func():

    #Specify the method and return value to replace only during this with scope execution
    with patch("Test.Widget.Additional", return_value=10) as m:
        import Test
        w = Test.Widget()
        print w.Additional(1)
        assert w.Additional(1) == 10
        print m()

if __name__ == '__main__':
    test_func()

Class type specification

mock = Mock(spec=SomeClass)
isinstance(mock, SomeClass) #This succeeds

Give Mock specific attributes

# coding: UTF-8
from mock import Mock, patch

if __name__ == '__main__':
    #A method that returns 3 with the name method,
    #A method that raises a KeyError exception with the name other
    attrs = {'method.return_value': 3, 'other.side_effect': KeyError}

    #You can add attributes at the same time as declaring(some_attribute)。
    mock = Mock(some_attribute='eggs', **attrs)

    print mock.some_attribute
    print mock.method()
    print mock.other()

Other

Another thing that seems to be useful

@patch('sys.stdout', new_callable=StringIO)

So, it will be created as a StringIO object at the time of creation.

You can write a pseudo class for testing and assign it with "Test.Widget =" without using mock. If you don't use the mock's validation feature, that may be faster.

django-webtest module

Very useful for testing Django.

pip install webtest
pip install django-webtest

The Django application looks like this:

mysite
|-mysite
|   |-__init__.py
|   |-settings.py
|   |-urls.py
|   |-wsgi.py
|-test app
|   |-__init__.py
|   |-form.py
|   |-modes.py
|   |-views.py
|   |-tests.py Write this test in this file|
|-templates
|-manage.py

Write the contents of tests.py as follows

from django_webtest import WebTest

class TestIndex(WebTest):

    def test_index(self):
        res = self.app.get("/") #Get the response

        #Check the status code and content.
        assert res.status == '200 OK'
        assert 'html' in res

After starting the development server with manage.py runserver, run the test with the following command.

# sudo python manage.py test testapp

Output result

Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.175s

OK
Destroying test database for alias 'default'...

Recommended Posts

Python debug and test module
Cooperation between python module and API
[Code] Module and Python version output
Python3 socket module and socket communication flow
Python module import
Implementation module "deque" in queue and Python
Distribution and test
Python Integrity Test
Python collections module
Module import and exception handling in python
[Python] Class type and usage of datetime module
[python] Compress and decompress
Primality test by Python
Primality test with Python
Python and numpy tips
[Python] pip and wheel
Python module (Python learning memo ④)
Create a Python module
Batch design and python
python original module import
Python packages and modules
Python test package memo
Vue-Cli and Python integration
Ruby, Python and map
Primality test with python
python input and output
Python and Ruby split
python tag integration test
Python3, venv and Ansible
python unit test template
Python asyncio and ContextVar
Sample of getting module name and class name in Python
[Python of Hikari-] Chapter 08-03 Module (Import and use of standard library)
Programming with Python and Tkinter
Design and test Verilog in Python only with Veriloggen and cocotb.
Encryption and decryption with Python
Sort Python module imports alphabetically
Python: Class and instance variables
How Python module import works
3-3, Python strings and character codes
Python 2 series and 3 series (Anaconda edition)
Python on Ruby and angry Ruby on Python
Python indentation and string format
Python real division (/) and integer division (//)
Install Python and Flask (Windows 10)
[Python] ModuleNotFoundError: No module named'urlparse'
About python objects and classes
About Python variables and objects
Algorithm in Python (primality test)
Python module num2words Difference in behavior between English and Russian
Apache mod_auth_tkt and Python AuthTkt
Å (Ongustromu) and NFC @ Python
About the Python module venv
Understand Python packages and modules
# 2 [python3] Separation and comment out
Python shallow copy and deep copy
Python and ruby slice memo
Python installation and basic grammar
Python shallow and deep copy
Debug Python with VS Code
About Python, len () and randint ()