Giter Club home page Giter Club logo

trace_example's Introduction

Python Trace Examples

软件测试的代码覆盖率与执行路径分析。网络上比较多的是利用已经实现好的包 coverage 来做,或者利用 Pythontrace 库来做, 参考 trace — 跟踪代码执行流, trace --- 跟踪Python语句执行, 但是可以利用 sys.settrace() 通过插桩自行实现, 参考 python插装--sys.settrace

Coverage

首先运行测试,当前目录生成 .covetage 文件

coverage run test.py
coverage run --branch test.py

终端输出

This is the main program.
Right!
recurse(2)
recurse(1)
recurse(0)
ZeroDivisionError: division by zero

其次运行解析

coverage report

终端输出结果

Name         Stmts   Miss Branch BrPart  Cover
----------------------------------------------
recurse.py       7      1      2      0    89%
test.py         15      3      6      2    67%
----------------------------------------------
TOTAL           22      4      8      2    73%

运行解析,当前目录生成 htmlcov/ 目录,点击 index.html 打开网页

coverage html

trace

首先运行解析,忽略 import_lib 库

python -m trace --ignore-dir=D:\Anaconda --trace --timing main.py
python -m trace --ignore-dir=/mnt/hdd4T/dlw_home/anaconda3 --trace --timing main.py

终端输出结果

 --- modulename: main, funcname: <module>
0.00 main.py(3): '''
0.00 main.py(12): from recurse import recurse
 --- modulename: recurse, funcname: <module>
0.01 recurse.py(3): '''
0.01 recurse.py(12): def recurse(level):
0.01 recurse.py(17): def not_called():
0.01 main.py(14): def main():
0.01 main.py(30): if __name__ == '__main__':
0.01 main.py(31):     main()
 --- modulename: main, funcname: main
0.01 main.py(15):     print('This is the main program.')
This is the main program.
0.01 main.py(16):     if 2+3 == 5:
0.01 main.py(17):         print("Right!")
Right!
0.01 main.py(21):     recurse(2)
 --- modulename: recurse, funcname: recurse
0.01 recurse.py(13):     print('recurse({})'.format(level))
recurse(2)
0.01 recurse.py(14):     if level:
0.01 recurse.py(15):         recurse(level - 1)
 --- modulename: recurse, funcname: recurse
0.01 recurse.py(13):     print('recurse({})'.format(level))
recurse(1)
0.01 recurse.py(14):     if level:
0.01 recurse.py(15):         recurse(level - 1)
 --- modulename: recurse, funcname: recurse
0.01 recurse.py(13):     print('recurse({})'.format(level))
recurse(0)
0.01 recurse.py(14):     if level:
0.01 main.py(23):     try:
0.01 main.py(24):         a = 2 / 0
0.01 main.py(25):     except ZeroDivisionError:
0.01 main.py(26):         print("ZeroDivisionError: division by zero")
ZeroDivisionError: division by zero

运行解析,结果保存在 ./coverdir/

python -m trace --ignore-dir=/mnt/hdd4T/dlw_home/anaconda3 --count --coverdir ./coverdir --file ./coverdir/coverage_report.dat main.py

终端输出

This is the main program.
Right!
recurse(2)
recurse(1)
recurse(0)
ZeroDivisionError: division by zero

分析代码执行覆盖率输出百分比

python -m trace --coverdir ./coverdir --report --summary --missing --file ./coverdir/coverage_report.dat main.py

输出百分比

lines   cov%   module   (path)
   16    81%   main   (main.py)
    7    85%   recurse   (/mnt/hdd4T/dlw_home/pythonWorkspace/trace_example/trace_demo/recurse.py)

分析代码的调用关系

python -m trace --listfuncs --trackcalls main.py | grep -v importlib
python -m trace --listfuncs --trackcalls main.py

终端输出

This is the main program.
Right!
recurse(2)
recurse(1)
recurse(0)
ZeroDivisionError: division by zero

calling relationships:

*** /mnt/hdd4T/dlw_home/anaconda3/lib/python3.8/trace.py ***
  --> main.py
    trace.Trace.runctx -> main.<module>

*** /mnt/hdd4T/dlw_home/pythonWorkspace/trace_example/trace_demo/recurse.py ***
    recurse.recurse -> recurse.recurse

  --> /mnt/hdd4T/dlw_home/pythonWorkspace/trace_example/trace_demo/recurse.py


*** main.py ***
    main.<module> -> main.main
  --> /mnt/hdd4T/dlw_home/pythonWorkspace/trace_example/trace_demo/recurse.py
    main.main -> recurse.recurse

sys.settrace()

程序执行结果

>>> call, (sys_trace.py, __main__, 94, collect), (mycode.py, mycode, 28, main_func, {}, None)
>>> line, (sys_trace.py, __main__, 94, collect), (mycode.py, mycode, 29, main_func, {}, None)
This is the main program.
>>> line, (sys_trace.py, __main__, 94, collect), (mycode.py, mycode, 30, main_func, {}, None)
>>> call, (mycode.py, mycode, 30, main_func), (mycode.py, mycode, 17, add, {'a': 2, 'b': 3}, None)
>>> line, (mycode.py, mycode, 30, main_func), (mycode.py, mycode, 18, add, {'a': 2, 'b': 3}, None)
>>> line, (mycode.py, mycode, 30, main_func), (mycode.py, mycode, 19, add, {'a': 2, 'b': 3, 'c': 5}, None)
>>> return, (mycode.py, mycode, 30, main_func), (mycode.py, mycode, 19, add, {'a': 2, 'b': 3, 'c': 5}, 5)
>>> line, (sys_trace.py, __main__, 94, collect), (mycode.py, mycode, 31, main_func, {}, None)
Right!
>>> line, (sys_trace.py, __main__, 94, collect), (mycode.py, mycode, 35, main_func, {}, None)
>>> call, (mycode.py, mycode, 35, main_func), (mycode.py, mycode, 22, loop, {'level': 2}, None)
>>> line, (mycode.py, mycode, 35, main_func), (mycode.py, mycode, 23, loop, {'level': 2}, None)
recurse(2)
>>> line, (mycode.py, mycode, 35, main_func), (mycode.py, mycode, 24, loop, {'level': 2}, None)
>>> line, (mycode.py, mycode, 35, main_func), (mycode.py, mycode, 25, loop, {'level': 2}, None)
>>> call, (mycode.py, mycode, 25, loop), (mycode.py, mycode, 22, loop, {'level': 1}, None)
>>> line, (mycode.py, mycode, 25, loop), (mycode.py, mycode, 23, loop, {'level': 1}, None)
recurse(1)
>>> line, (mycode.py, mycode, 25, loop), (mycode.py, mycode, 24, loop, {'level': 1}, None)
>>> line, (mycode.py, mycode, 25, loop), (mycode.py, mycode, 25, loop, {'level': 1}, None)
>>> call, (mycode.py, mycode, 25, loop), (mycode.py, mycode, 22, loop, {'level': 0}, None)
>>> line, (mycode.py, mycode, 25, loop), (mycode.py, mycode, 23, loop, {'level': 0}, None)
recurse(0)
>>> line, (mycode.py, mycode, 25, loop), (mycode.py, mycode, 24, loop, {'level': 0}, None)
>>> return, (mycode.py, mycode, 25, loop), (mycode.py, mycode, 24, loop, {'level': 0}, None)
>>> return, (mycode.py, mycode, 25, loop), (mycode.py, mycode, 25, loop, {'level': 1}, None)
>>> return, (mycode.py, mycode, 35, main_func), (mycode.py, mycode, 25, loop, {'level': 2}, None)
>>> line, (sys_trace.py, __main__, 94, collect), (mycode.py, mycode, 37, main_func, {}, None)
>>> line, (sys_trace.py, __main__, 94, collect), (mycode.py, mycode, 38, main_func, {}, None)
>>> exception, (sys_trace.py, __main__, 94, collect), (mycode.py, mycode, 38, main_func, {}, (<class 'ZeroDivisionError'>, ZeroDivisionError('division by zero'), <traceback object at 0x000001DE1B10F580>))
>>> line, (sys_trace.py, __main__, 94, collect), (mycode.py, mycode, 39, main_func, {}, None)
>>> line, (sys_trace.py, __main__, 94, collect), (mycode.py, mycode, 40, main_func, {}, None)
ZeroDivisionError: division by zero
>>> return, (sys_trace.py, __main__, 94, collect), (mycode.py, mycode, 40, main_func, {}, None)

代码执行路径分析

===========================
No || Code 
---------------------------
28 || def main_func():
29 ||     print('This is the main program.')
30 ||     if add(2, 3) == 5:
17 || def add(a, b):
18 ||     c = a + b
19 ||     return c
31 ||         print("Right!")
35 ||     loop(2)
22 || def loop(level):
23 ||     print('recurse({})'.format(level))
24 ||     if level > 0:
25 ||         loop(level - 1)
22 || def loop(level):
23 ||     print('recurse({})'.format(level))
24 ||     if level > 0:
25 ||         loop(level - 1)
22 || def loop(level):
23 ||     print('recurse({})'.format(level))
24 ||     if level > 0:
37 ||     try:
38 ||         a = 2 / 0
39 ||     except ZeroDivisionError:
40 ||         print("ZeroDivisionError: division by zero")

代码覆盖率分析

===========================
Cnt | No || Code 
---------------------------
[0] | 13 || def not_called():
[0] | 14 ||     print('This function is never called.')
[1] | 17 || def add(a, b):
[1] | 18 ||     c = a + b
[1] | 19 ||     return c
[3] | 22 || def loop(level):
[3] | 23 ||     print('recurse({})'.format(level))
[3] | 24 ||     if level > 0:
[2] | 25 ||         loop(level - 1)
[1] | 28 || def main_func():
[1] | 29 ||     print('This is the main program.')
[1] | 30 ||     if add(2, 3) == 5:
[1] | 31 ||         print("Right!")
[0] | 32 ||     elif add(2, 3) == 4:
[0] | 33 ||         print("Wrong.")
[1] | 35 ||     loop(2)
[1] | 37 ||     try:
[1] | 38 ||         a = 2 / 0
[1] | 39 ||     except ZeroDivisionError:
[1] | 40 ||         print("ZeroDivisionError: division by zero")
[0] | 41 ||     else:
[0] | 42 ||         print("Successfully!")
[0] | 45 || if __name__ == '__main__':
[0] | 46 ||     main_func()
Total lines: 24, covered lines: 16, cover rate: 0.67.

模块调用关系分析

===========================
C/R | Module
---------------------------
 C  | main_func
 C  | add
 R  | main_func
 C  | loop
 C  | loop
 C  | loop
 R  | loop
 R  | loop
 R  | main_func
 R  | collect

trace_example's People

Stargazers

 avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.