week 10 summary

Gergő Pintér, PhD

gergo.pinter@uni-corvinus.hu

V model [1]

  • each phase has output and a review process
    • errors are found at early stage
    • decreases the risk of failure
  • testing is done in a hierarchical perspective
  • review is a testing process usually without executing the code

test pyramid

the turtle and rabbit figures by Delapouite under CC BY 3.0 via game-icons.net

what is a unit?

  • defined as a single behaviour exhibited by the system under test
    • usually corresponding to a requirement
  • often corresponds to a function or a module / method or a class
    • depending on the paradigm
    • often, but not always
  • “only entry points to externally-visible system behaviours define units”
    • by Kent Beck [2]

a unit test is another piece of code, that tests the given unit

arrange, act, assert(, annihilate) pattern

parts of a unit test

arrange
set up the testing environment (e.g., create objects)
act
call the tested unit
assert
compare the result of the ‘act’ step to the expected value
(annihilate)
free resources
automatic in modern languages
def test_fizzbuzz():
    # arrange
    test_input = 3
    # act
    result = fizzbuzz(test_input)
    # assert
    assert result == "Fizz"

mocking

  • the whole unit test suite should be able to run in milliseconds
    • to give immediate feedback
  • slow elements of the software should be mocked
    • e.g., database, network connection
  • part of arrange step

test doubles – mock object types

there is no open standard for categories

  • dummy
  • stub
  • spy
  • mock
  • fake
reproduction of figure 2 from [3]

these are from the book xUnit test patterns: Refactoring test code – by Gerard Meszaros [4]

test-driven development (TDD)

  • write test before writing the tested code
  • without the called unit the test fill fail
    • the called function does not exist
  • write code, that makes the test pass
  • improve the code quality
    • e.g., make it clear and clean
    • both the test and tested code

As the tests get more specific, the code gets more generic.

– Robert C. Martin, The Cycles of TDD [5]

test coverage

  • the percentage of the code lines ‘protected’ or covered by tests

code/fizzbuzz.py

def fizzbuzz(i: int) -> str:
    result = ""
    if i % 15 == 0:
        result += "FizzBuzz"
    elif i % 3 == 0:
        result += "Fizz"
    elif i % 5 == 0:
        result += "Buzz"
    else:
        result = str(i)
    return result
def fizzbuzz(i: int) -> str:
    result = ""
    if i % 15 == 0:
        result += "FizzBuzz"
    elif i % 3 == 0:
        result += "Fizz"
    elif i % 5 == 0:
        result += "Buzz"
    else:
        result = str(i)
    return result
def fizzbuzz(i: int) -> str:
    result = ""
    if i % 15 == 0:
        result += "FizzBuzz"
    elif i % 3 == 0:
        result += "Fizz"
    elif i % 5 == 0:
        result += "Buzz"
    else:
        result = str(i)
    return result

code/test_fizzbuzz.py

from fizzbuzz import fizzbuzz


def test_fizzbuzz():
    assert fizzbuzz(15) == "FizzBuzz"
    assert fizzbuzz(3) == "Fizz"
from fizzbuzz import fizzbuzz


def test_fizzbuzz():
    assert fizzbuzz(15) == "FizzBuzz"
    assert fizzbuzz(3) == "Fizz"
    assert fizzbuzz(5) == "Buzz"
from fizzbuzz import fizzbuzz


def test_fizzbuzz():
    assert fizzbuzz(15) == "FizzBuzz"
    assert fizzbuzz(3) == "Fizz"
    assert fizzbuzz(5) == "Buzz"
    assert fizzbuzz(17) == "17"

test coverage: 70%

test coverage: 90%

test coverage: 100%

four control flow branch, all of them needs to be tested

how to measure code quality?

it is hard to objectively measure the quality of code

  • number of source lines of code (SLOC)
  • style guide compliance – is the code clean?
  • Halstead metrics
  • cyclomatic complexity – is the code simple?
  • maintainability index
  • test coverage – is the code tested?

what to test - the edge cases

def calculate_progress(
    finished: int,
    total: int,
    as_percentage: bool,
) -> float:
    progress = finished / total

    if as_percentage:
        return progress * 100
    else:
        return progress

this function need some value checking

what does this function do?
  • divides the number of finished lessons by the total number of lessons
  • returns progress in the closed interval of [0, 1] or [0, 100]
edge cases
  • total is 0
  • total is less than 0
  • finished is less than 0
  • finished is greater than total

test coverage only measures that every control flow branch is tested

the point of testing is testing for the edge cases

changing the software

add feature fix a bug refactor optimize
structure changes changes changes
new funcionality changes
functionality changes
resource usage changes

Michael Feathers, Working Effectively with Legacy Code: part 1 pp 6 [6]

testing approaches

black box

  • examining / testing the functionality without knowing the inner structure
  • works at all levels: unit, integration, system, acceptance
  • also for debugging a legacy code

white box

  • testing the internal structure as opposed to its functionality
  • often associated to unit testing, but also works on higher levels (i.e., integration, system)

references

[1]
K. Forsberg and H. Mooz, “The relationship of system engineering to the project cycle,” Center for Systems Management, vol. 5333, 1991.
[2]
K. Beck, Test driven development: By example. Addison-Wesley Professional, 2002.
[3]
[4]
G. Meszaros, xUnit test patterns: Refactoring test code. Pearson Education, 2007.
[5]
R. C. Martin, “The cycles of TDD.” http://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html , 17-Dec-2014.
[6]
M. Feathers, Working effectively with legacy code. Prentice Hall Professional, 2004.