Codecademy Logo

Software Engineering in Python II

unittest Module

In Python, the unittest module from the standard library provides a framework for unit testing.

It can be imported with import unittest.

import unittest

Skip with unittest

In Python’s unittest framework, individual test methods can be skipped by using the skip, skipIf, or skipUnless decorators, as well as the TestCase .skipTest method.

The skipUnless option skips the test if the condition evaluates to False. The skipIf option skips the test if the condition evaluates to True.

In the code example, both skipUnless() and skipIf() skip the test if the operating system is not Windows.

class Tests(unittest.TestCase):
@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
def test_linux_feature(self):
print("This test should only run on windows")
@unittest.skipIf(not sys.platform.startswith("win"), "requires Windows")
def test_other_linux_feature(self):
print("This test should only run on windows")

expectedFailure with unittest

In Python’s unittest framework, individual tests can be marked as an expected failure using the expectedFailure decorator. With this decorator, the test will be considered successful in test results if it fails and will be marked as a failure if it passes.

In the code example, the .expectedFailure decorator is used, and therefore, the test_something_that_fails() test will pass if assertEqual(0,1) fails (it indeed does fail and the test as a whole passes).

class Tests(unittest.TestCase):
@unittest.expectedFailure
def test_something_that_fails(self):
self.assertEqual(0, 1)

Test Fixture

In Python, a test fixture is a mechanism for ensuring that proper test setup and teardown occur.

Test fixtures can be created by adding setUp() and tearDown() methods to the TestCase child class. Text fixtures created like this run before and after each individual test.

In the code example, 'This runs before each test' will be printed before each test in the class, and 'This runs after each test' will be printed after each test.

class Tests(unittest.TestCase):
def setUp(self):
print('This runs before each test')
def test_equality(self):
self.assertEqual(1, 1)
def test_list(self):
self.assertIn(1, [1, 2, 3])
def tearDown(self):
print('This runs after each test')

SyntaxError

A SyntaxError is reported by the Python interpreter when some portion of the code is incorrect. This can include misspelled keywords, missing or too many brackets or parentheses, incorrect operators, missing or too many quotation marks, or other conditions.

age = 7 + 5 = 4
File "<stdin>", line 1
SyntaxError: can't assign to operator

Computer Programs

A computer program is a collection of instructions used to perform a certain task.

Computer Process

A process is an abstraction used to represent a program while it is in execution.

Process States

A process typically exists in five states: New, Ready, Running, Blocked, or Finished

Context Switching

Context switching allows CPU cores to alternate between ready and blocked processes to best take advantage of limited computing resources.

Preemption

Preemption occurs when a process is temporarily interrupted by an external scheduler to prioritize a more important task.

Blocked Process

A process is blocked when it has to wait for a contested, limited, or slow resource, such as accessing a specific file or waiting for a network request.

Process Layout

The layout of a process in memory has four distinct sections:

  • A text section for the compiled code
  • A data section for initialized variables
  • A stack for local variables
  • A heap for dynamic memory allocation

Process Control Block

Every process is initialized with a process control block that is required by the operating system to be able to identify and control the process.

Process Parent-Child Relationship

When a process launches another process, the original process enters a parent-child relationship with the new process. This relationship facilitates the sharing of common data and signals along the hierarchy as well as the arrangement of which process may terminate first.

Process Thread

A thread represents the sequence of programmed instructions that are actively being executed. They share resources which allows for faster communication and context switching as well as requiring fewer system resources when compared to processes.

Process Multithreading

Multithreading is the capability for a single CPU core to execute multiple threads at once. This improves system utilization and responsiveness by more efficiently splitting up tasks

Multiple threads existing within a process with local and shared memory

Kernel Threads

Kernel threads are threads created in kernel space using kernel code and libraries through a system call. The kernel is fully aware of these threads and can properly manage them.

User Threads

User threads are threads created in user space using local code and function calls. The kernel is not aware of these threads and cannot directly control them. User threads allow for more fine-grained control by developers and are more efficient than kernel threads as they do not need to make system calls.

User vs Kernel Threads

User threads can be mapped to kernel threads in a variety of ways: 1:1 Kernel-Level threading, N:1 User-Level threading, or M:N Hybrid threading.

assert Keyword

In Python, the assert keyword is used to test that a condition is met. If the condition evaluates to False, an AssertionError is raised with the specified error message.

The example code would produce the following output:

Traceback (most recent call last):
  File "/Users/me/Desktop/exceptions.py", line 2, in <module>
    assert number == 3, "number is not equal to 3!"
AssertionError: Number is not equal to 3!
number = 5
assert number == 3, 'Number is not equal to 3!'

Unit Test

In Python, a unit test validates the behavior of a single isolated component, such as a function.

Often, a function is tested for its expected return value by giving it one or two extreme inputs and one normal use case.

Test Case from unittest.TestCase

In Python, a test case is an individual test for an expected response to a given set of inputs.

In the code example, notice how test cases are created by defining a class that inherits from unittest.TestCase. Then, tests are added by defining methods that start with the word test.

class MyTests(unittest.TestCase):
def test_example(self):
print('This is a test method that will pass')

Test Runner with main()

In Python, a test runner is a component that collects and executes tests and then provides results to the user.

Tests can be run by calling unittest.main() in a module that has a test class derived from unittest.TestCase.

class MyTests(unittest.TestCase):
def test_example(self):
print('This is a test method that will pass')
unittest.main()

The example code would output:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

assertEqual() from unittest

In Python’s unittest framework, the assertEqual TestCase method is used to test that two values are equal to each other.

In the code example, .assertEqual() is checking if output (which is add_five(1)) is equal to 6.

class Tests(unittest.TestCase):
def test_add_five(self):
output = add_five(1)
self.assertEqual(output, 6)

assertIn() from unittest

In Python’s unittest framework, the assertIn TestCase method is used to test that a specific item is present in a container.

In the example code, .assertIn() checks if 'red' is present in colors. In this case, the test will fail since 'red' is not included in the list returned by get_rainbow_colors().

def get_rainbow_colors():
return ['orange', 'yellow', 'green', 'blue', 'indigo', 'violet']
class Tests(unittest.TestCase):
def test_get_rainbow_colors(self):
colors = get_rainbow_colors()
self.assertIn('red', colors)

assertRaises() from unittest

In Python’s unittest framework, the assertRaises TestCase method is used to test that a specific exception is raised.

The assertRaises method takes an exception type as its first argument, a function reference as its second, and an arbitrary number of arguments as the rest.

In the code example, the assertRaises() test calls the raise_index_error() function. If an IndexError exception is raised, the test passes. If any other type of exception is raised or if no exception is raised, the test will fail.

class Tests(unittest.TestCase):
def test_raises_index_error(self):
self.assertRaises(IndexError, raise_index_error)

Test Fixtures with setUpClass() and tearDownClass()

In Python’s unittest framework, test fixtures can be created by adding setUpClass() and tearDownClass() methods to the TestCase child class. Text fixtures created like this run only once - before and after all tests in the class are executed. These methods must be decorated with the classmethod decorator.

In the code example, 'This runs once before all tests' will be printed once before all the tests in the class, and 'This runs once after all tests' will be printed after all the tests.

class Tests(unittest.TestCase):
@classmethod
def setUpClass(self):
print('This runs once before all tests')
def test_equality(self):
self.assertEqual(1, 1)
def test_list(self):
self.assertIn(1, [1, 2, 3])
@classmethod
def tearDownClass(self):
print('This runs once after all tests')

Learn More on Codecademy