Skip to content

Instantly share code, notes, and snippets.

@misho-kr
Last active December 26, 2023 07:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save misho-kr/f62692d9d5e4ef1f9348c7553d4784ec to your computer and use it in GitHub Desktop.
Save misho-kr/f62692d9d5e4ef1f9348c7553d4784ec to your computer and use it in GitHub Desktop.
Summary of "Introduction to Testing in Python" from DataCamp.Com

Learn the very basics of creating tests in Python. Meet four types of software testing methods. Know the types of testing methods, and choose the most suitable ones for a specific context. Design tests and implement them in Python using the pytest and the unittest libraries.

Creating Tests with pytest

  • Why is testing so important?
  • What is testing?
  • Testing with pytest - a simple example
    • Functon-based - searches for scripts and functions starting with test_
  • Context managers
  • Example CLI run
    • Keyword argument, by passing -k "keyword_expression"
import pytest

# A function to test
def squared(number):
  return number * number
  
# A test function always starts with "test"
def test_squared():
  assert squared(-2) == squared(2)

# A test function
def test_raises():
  with pytest.raises(ZeroDivisionError):
    division(a=25, b=0)
> pytest func.py
  • Test marker - a tag (a marker) of a test in the pytest library
    • skip the test if the condition met
    • test is expected to fail
# The skip marker example
@pytest.mark.skip
def test_get_len():
  assert get_length('123') == 3

# The skipif marker example
@pytest.mark.skipif('2 * 2 == 5')
def test_get_len():
  assert get_length('abc') == 3

# The xfail marker example
@pytest.mark.xfail
def test_gen_seq():
  assert gen_sequence(-1)

Pytest Fixtures

  • Fixture - a prepared environment that can be used for a test execution
    • To make test setup easier
    • To isolate the test of the environmental preparation
    • To make the fixture code reusable
  • Fixture Setup - preparing the environment and resources that are required by one or more tests
import pytest

# Fixture decorator
@pytest.fixture

# Fixture for data initialization
def data():
  return [0, 1, 1, 2, 3, 5, 8, 13, 21]

def test_list(data):
  assert len(data) == 9
  assert 5 in data
  • Chain fixtures requests - allows a fixture to use another fixture
    • Establish dependencies between fixtures
    • Keep the code modular
# Fixture that is requested by the other fixture
@pytest.fixture
def setup_data():
  return "I am a fixture!"

# Fixture that is requested by the test function
@pytest.fixture
def process_data(setup_data):
  return setup_data.upper()
  • Autouse argument
    • When autouse=True the fixture function is executing regardless of a request
    • Helps to reduce the number of redundant fixture calls, thus makes the code simpler
    • When to use: in case we need to apply certain environment preparations or modifications
# Autoused fixture
@pytest.fixture(autouse=True)
def set_pd_options():
  pd.set_option('display.max_columns', 5000)

# Test function
def test_pd_options():
  assert pd.get_option('display.max_columns') == 5000
  • Fixture Teardown
    • a process of cleaning up ("tearing down") resources that were allocated or created during the setup of a testing environment
    • It is important to clean the environment at the end of a test
@pytest.fixture
def init_list():
  return []

@pytest.fixture(autouse=True)
def add_numbers_to_list(init_list):
  # Fixture Setup
  init_list.extend([i for i in range(10)])
  
  # Fixture output
  yield init_list
  
  # Teardown statement
  init_list.clear()

def test_9(init_list):
  assert 9 in init_list

Basic Testing Types

  • Unit testing - software testing method
    • Foundation for testing "the bigger picture" of the software
    • Unit - the smallest working part that can be tested
  • Test case - a set of unit inputs and expected outputs
    • Summarizes a particular piece of the problem
# Test Case 1: regular array
def test_regular():
  assert sum_of_arr([1, 2, 3]) == 6
  assert sum_of_arr([100, 150]) == 250

# Test Case 2: empty list
def test_empty():
  assert sum_of_arr([]) == 0

# Test Case 3: one number
def test_one_number():
  assert sum_of_arr([10]) == 10
  assert sum_of_arr([0]) == 0
  • Feature testing with pytest
    • A software system functionality, satisfies a particular user's requirement
    • Are wider that units
    • One unit does not work - does not mean the feature is NOT OK
    • All units work as expected - does not mean the feature is OK
  • Integration testing with pytest
    • Software testing method, that allows to verify that an interaction behaves normally
    • Integration - an interaction between 2 or more modules inside of a system
  • Performance testing with pytest
    • A type of testing that measures software performance
    • Performance - how efficiently does software utilizes the resources of the system to accomplish a task
  • Benchmark fixture
> pip install pytest-benchmark
> pytest Example_1.py
# Example_1.py
import time

def test_func(benchmark):
  benchmark(time.sleep, 1)

# Example_2.py
def test_func(benchmark):
  @benchmark
  def sleep_for_1_sec():
     time.sleep(1)

Writing tests with unittest

  • Recap of OOP
  • unittest - built-in Python framework for test automation
    • Based on OOP: each test case is a class, and each test is a method
    • Declare a class inheriting from unittest.TestCase
    • Assertion methods
  • unittest -k - run test methods and classes that match the pattern or substring
  • unittest -f - stop the test run on the first error or failure
  • unittest -c - Catch flag, lets to interrupt the test by pushing "Ctrl - C"
  • unittest -v - run tests with more detail
import unittest

# Declaring the TestCase class
class TestSquared(unittest.TestCase):
  # Defining the test
  def test_negative(self):
    self.assertEqual((-3) ** 2, 9)
> python3 -m unittest test_sqneg.py
> python3 -m unittest -k "SomeStringOrPattern" test_script.py
> python3 -m unittest -f test_script.py
  • Fixture in unittest - the preparaton needed to perform one or more tests
    • setUp() method called to prepare the test fixture before the actual test
    • tearDown() method called after the test method to clean the environment
import unittest

class TestLi(unittest.TestCase):

  # Fixture setup method
  def setUp(self):
    self.li = [i for i in range(100)]
  # Fixture teardown method
  def tearDown(self):
    self.li.clear()

  # Test method
  def test_your_list(self):
    self.assertIn(99, self.li)
    self.assertNotIn(100, self.li)
  • Practical examples
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment