Mutation Testing in Python Austin Bingham @austin_bingham - - PowerPoint PPT Presentation

mutation testing in python
SMART_READER_LITE
LIVE PREVIEW

Mutation Testing in Python Austin Bingham @austin_bingham - - PowerPoint PPT Presentation

Mutation Testing in Python Austin Bingham @austin_bingham @sixty_north 1 Sunday, October 4, 15 2 Sunday, October 4, 15 3 Sunday, October 4, 15 Mutation Testing 4 Sunday, October 4, 15 What is mutation testing? Code under test + test


slide-1
SLIDE 1

1

Mutation Testing in Python

Austin Bingham

@austin_bingham

@sixty_north

Sunday, October 4, 15
slide-2
SLIDE 2

2

Sunday, October 4, 15
slide-3
SLIDE 3

3

Sunday, October 4, 15
slide-4
SLIDE 4

4

Mutation Testing

Sunday, October 4, 15
slide-5
SLIDE 5

What is mutation testing?

5

Code under test + test suite Introduce single change to code under test Run test suite Ideally, all changes will result in test failures

Sunday, October 4, 15
slide-6
SLIDE 6

A nested loop of mutation and testing

Basic algorithm

for operator in mutation-operators: for site in operator.sites(code):

  • perator.mutate(site)

run_tests()

6

Sunday, October 4, 15
slide-7
SLIDE 7

What does mutation testing tell us?

7

Killed

Tests properly detected the mutation.

Incompetent

Mutation produced code which is inherently fmawed.

Survived

Tests failed to detect the mutant! Tests are inadequate for detecting defects in necessary code either Mutated code is extraneous

  • r
Sunday, October 4, 15
slide-8
SLIDE 8

8

Sunday, October 4, 15
slide-9
SLIDE 9

Do my tests meaningfully cover my code's functionality

Goal #1: Coverage analysis

Is a line executed?

versus

Is functionality verified?

9

Sunday, October 4, 15
slide-10
SLIDE 10

Survivors can indicate code which is no longer necessary

Goal #2: Detect unnecessary code

10

Sunday, October 4, 15
slide-11
SLIDE 11

Examples of mutations

11

  • AOD - arithmetic operator deletion
  • AOR - arithmetic operator replacement
  • ASR - assignment operator replacement
  • BCR - break continue replacement
  • COD - conditional operator deletion
  • COI - conditional operator insertion
  • CRP - constant replacement
  • DDL - decorator deletion
  • EHD - exception handler deletion
  • EXS - exception swallowing
  • IHD - hiding variable deletion
  • IOD - overriding method deletion
  • IOP - overridden method calling position change
  • LCR - logical connector replacement
  • LOD - logical operator deletion
  • LOR - logical operator replacement
  • ROR - relational operator replacement
  • SCD - super calling deletion
  • SCI - super calling insert
  • SIR - slice index remove

Replace relational operator

x > 1 x < 1

break/continue replacement

break continue

Sunday, October 4, 15
slide-12
SLIDE 12

Long test suites, large code bases, and many operators can add up

Complexity #1: It takes a loooooooong time

12

Image credit: John Mainstone (CC BY-SA 3.0)

What to do?

  • Parallelize as much as possible!
  • After baselining:
  • only run tests on modified code
  • only mutate modified code
  • Speed up test suite
Sunday, October 4, 15
slide-13
SLIDE 13

Some incompetent mutants are harder to detect that others

Complexity #2: Incompetence detection

13

"Good luck with that."

Alan Turing (apocryphal)

Sunday, October 4, 15
slide-14
SLIDE 14

Some mutants have no detectable differences in functionality

Complexity #3: Equivalent mutants

14

def consume(iterator, n): """Advance the iterator n-steps ahead. If n is none, consume entirely.""" # Use functions that consume iterators at C speed. if n is None: # feed the entire iterator into a zero-length deque collections.deque(iterator, maxlen=0) else: # advance to the empty slice starting at position n next(islice(iterator, n, n), None)

Sunday, October 4, 15
slide-15
SLIDE 15

15

Cosmic Ray: Mutation Testing for Python

Sunday, October 4, 15
slide-16
SLIDE 16

Sub-packages and modules are discovered automatically

Cosmic Ray operates on packages

16

find_modules.py

def find_modules(name): module_names = [name] while module_names: module_name = module_names.pop() try: module = importlib.import_module(module_name) yield module if hasattr(module, '__path__'): for _, name, _ in pkgutil.iter_modules(module.__path__): module_names.append('{}.{}'.format(module_name, name)) except Exception: # pylint:disable=broad-except LOG.exception('Unable to import %s', module_name)

Sunday, October 4, 15
slide-17
SLIDE 17

Support for tests systems are provided by dynamically discovered modules

Test system plugins

  • Using OpenStack's stevedore plugin system
  • Plugins can come from external packages
  • Define a TestRunner subclass and implement

the _run() method

  • Report a simple success or failure along with a

printable object containing more information

  • TestRunners are only given a "start directory"

as context

17

cosmic_ray py.test unittest

plugins

my_package my_test_system

Sunday, October 4, 15
slide-18
SLIDE 18

Standard library abstract syntax tree handling

ast

  • Generate AST from source code
  • Modify copies of ASTs using

ast.NodeTransformer

18

Sunday, October 4, 15
slide-19
SLIDE 19

Operators are responsible for actual AST modifjcation

Operators

  • Operators inherit from Operator which in

turn inherits from ast.NodeTransformer

  • Operators are provided as plugins
  • They have two primary jobs:
  • Identify locations where a mutation can occur
  • Perform the mutation open request

19

+ 1 2

  • perator
  • 1

2

Sunday, October 4, 15
slide-20
SLIDE 20

Converts unary-sub to unary-add

Example operator: Reverse unary subtraction

20

class ReverseUnarySub(Operator): def visit_UnaryOp(self, node): if isinstance(node.op, ast.USub): return self.visit_mutation_site(node) else: return node def mutate(self, node): node.op = ast.UAdd() return node

Sunday, October 4, 15
slide-21
SLIDE 21

Python provides a sophisticated system for performing module imports

Module management: overview

finders

Responsible for producing loaders when they recognize a module name

21

loaders

Responsible for populating module namespaces on import

sys.meta_path

A list of finders which are queried in order with module names when import is executed

Sunday, October 4, 15
slide-22
SLIDE 22

Cosmic Ray implements a custom finder

Module management: Finder

  • The finder associates module names

with ASTs

  • It produces loaders for those modules

which are under mutation

22

Sunday, October 4, 15
slide-23
SLIDE 23

Cosmic Ray implements a custom finder

Module management: Finder

23

class ASTFinder(MetaPathFinder): def __init__(self, fullname, ast): self._fullname = fullname self._ast = ast def find_spec(self, fullname, path, target=None): if fullname == self._fullname: return ModuleSpec(fullname, ASTLoader(self._ast, fullname)) else: return None

Sunday, October 4, 15
slide-24
SLIDE 24

Cosmic Ray implements a custom loader

Module management: Loader

  • The loader compiles its AST in the

namespace of a new module object

24

Sunday, October 4, 15
slide-25
SLIDE 25

Cosmic Ray implements a custom loader

Module management: Loader

25

class ASTLoader: def __init__(self, ast, name): self._ast = ast self._name = name def exec_module(self, mod): exec(compile(self._ast, self._name, 'exec'), mod.__dict__)

Sunday, October 4, 15
slide-26
SLIDE 26

Mutant isolation with multiple processes

multiprocessing

26

cosmic-ray Process 1 Mutant 1 Process 2 Mutant 2 Process 3 Mutant 3

  • Avoid cross-mutant interference
  • Signifjcantly simplifjes spawning

processes

Sunday, October 4, 15
slide-27
SLIDE 27

Actor model implementation in Python

pykka

27

  • Simplify design
  • Support concurrency
Sunday, October 4, 15
slide-28
SLIDE 28

Event loop to drive actors

asyncio

28

Sunday, October 4, 15
slide-29
SLIDE 29

Here's how we put all of these pieces together

Operational overview

  • 1. Find test-runner to use
  • 2. Identify modules for mutation
  • 3. Find all of the operators to use
  • 4. Lazily generate a sequence of mutated ASTs
  • 5. For each mutant, run the tests

1.Each test runs in a new process 2.Each run manipulates its location sys.meta_path to inject the correct module

29

Sunday, October 4, 15
slide-30
SLIDE 30

30

Demo

Sunday, October 4, 15
slide-31
SLIDE 31

Up to a two line subtitle, generally used to describe the takeaway for the slide

31

Sunday, October 4, 15
slide-32
SLIDE 32

32

Thank you!

@sixty_north

Austin Bingham

@austin_bingham

Sunday, October 4, 15