nose_allure描述中增加输出日志

python测试框架nose,在jenkins打印allure报告,界面友好,但是欠缺输出功能,为了以后排查问题使用,故修改源码

直接上源码:nose_allure中__init__.py

同时也需要修改:allure中common.py代码


里面有个bug是,输出的内容没有换行,很不友好,如果哪位大牛修改了请通知小弟。

输出的页面是: ps:qq截图粘帖图片后,提交了就没有了呢,


common.py 修改stop_case:代码如下

def stop_case(self, status, message=None, trace=None,description=None):
    """
    :arg status: one of :py:class:`allure.constants.Status`
    :arg message: error message from the test
    :arg trace: error trace from the test

    Finalizes with important data the test at the top of ``self.stack`` and returns it

    If either ``message`` or ``trace`` are given adds a ``Failure`` object to the test with them.
    """
    test = self.stack[-1]
    test.status = status
    test.stop = now()
    if description:
        test.description = description
    else:
        test.description=''
    if message or trace:
        test.failure = Failure(message=message, trace=trace or '')

    self.testsuite.tests.append(test)

    return test

nose_allure中__init__.py代码修改如下,修改内容自己比对

# -*- coding: utf-8 -*-

__author__ = "chipiga86@gmail.com"

import os
import traceback
from types import ModuleType
from functools import wraps
import sys
import nose
from allure.constants import Status, Label
from nose.plugins.base import Plugin
from nose.plugins.attrib import AttributeSelector
from nose.plugins.plugintest import MultiProcessFile
from .utils import AllureWrapper, get_labels
from io import StringIO
from nose.pyversion import force_unicode, format_exception
def run_only_when_suite_exist(func):
    """
    Decorator to disable method if allure doesnt have any active suites.
    This is needed to avoid of IndexError that hides the real exception.
    """
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        if self.allure.impl.stack:
            return func(self, *args, **kwargs)

    return wrapper

class Tee(object):
    def __init__(self, encoding, *args):
        self._encoding = encoding
        self._streams = args

    def write(self, data):
        data = force_unicode(data, self._encoding)
        for s in self._streams:
            s.write(data)

    def writelines(self, lines):
        for line in lines:
            self.write(line)

    def flush(self):
        for s in self._streams:
            s.flush()

    def isatty(self):
        return False

class Allure(Plugin):
    # name = 'xunit'
    # score = 1500
    encoding = 'UTF-8'
    # error_report_file = None
    test_suite = False
    def __init__(self):
        super(Allure, self).__init__()
        self._capture_stack = []
        self._currentStdout = None
        self._currentStderr = None

    def getvalue(self):
        print()

    def options(self, parser, env):
        super(Allure, self).options(parser, env)

        parser.add_option('--logdir', dest='logdir')
        parser.add_option('--not-clear-logdir', dest='not_clear_logdir',
                          action='store_true', default=False)
        parser.add_option('--feature', dest='feature')
        parser.add_option('--story', dest='story')
        parser.add_option('--issue', dest='issue')
        parser.add_option('--severity', dest='severity')

    def configure(self, options, conf):
        super(Allure, self).configure(options, conf)
        self.options = options

        if options.logdir:
            logdir = os.path.normpath(os.path.abspath(os.path.expanduser(
                os.path.expandvars(options.logdir))))

            if not os.path.isdir(logdir):
                os.makedirs(logdir)
            else:
                # Need to provide an option to skip dir cleaning due to multiprocess
                # plugin usage can lead to logdir cleaning at the end of testing.
                # Unfortunately not possible to detect is it child process or parent.
                # Otherwise possible to clean logdir only in parent process always.
                if not options.not_clear_logdir:
                    for file_name in os.listdir(logdir):
                        file_path = os.path.join(logdir, file_name)
                        if os.path.isfile(file_path):
                            os.unlink(file_path)

            self.allure = nose.allure = AllureWrapper(logdir)

        for label in 'feature', 'story', 'issue', 'severity':
            if getattr(options, label, None):

                if not getattr(options, 'attr', None):
                    options.attr = []

                get_attr = lambda l: "%s_%s=%s" % \
                    (Label.DEFAULT, getattr(Label, label.upper()), l.strip())
                attrs = map(get_attr, getattr(options, label).split(','))

                options.attr.extend(attrs)

        if options.attr:
            for plugin in self.conf.plugins.plugins:
                if isinstance(plugin, AttributeSelector):
                    plugin.configure(options, conf)
                    break

    def begin(self):
        if not self.conf.options.logdir:
            raise LookupError('Should provide "--logdir" argument!')

    def startContext(self, context):
        self._startCapture()

    def stopContext(self, context):
        self._endCapture()

    def beforeTest(self, test):
        """Initializes a timer before starting a test."""
        self._startCapture()

    def _endCapture(self):
        if self._capture_stack:
            sys.stdout, sys.stderr = self._capture_stack.pop()

    def afterTest(self, test):
        self._endCapture()
        self._currentStdout = None
        self._currentStderr = None


    def _startCapture(self):
        self._capture_stack.append((sys.stdout, sys.stderr))
        self._currentStdout = StringIO()
        self._currentStderr = StringIO()
        sys.stdout = Tee(self.encoding, self._currentStdout, sys.stdout)
        sys.stderr = Tee(self.encoding, self._currentStderr, sys.stderr)

    def _getCapturedStdout(self):
        if self._currentStdout:
            value = self._currentStdout.getvalue()
            return value
        return ''

    def startTest(self, test):
        if not self.test_suite:
            context_name = getattr(test.context, '__module__',
                                   test.context.__name__)
            self.allure.impl.start_suite(name=context_name,
                                         description=test.context.__doc__ or
                                         None)
            self.test_suite = True

        if hasattr(test.test, "test"):
            method = test.test.test

        else:
            method = getattr(test.test, test.test._testMethodName)

        hierarchy = ".".join(filter(None, test.address()[1:]))
        self.allure.impl.start_case(hierarchy,labels=get_labels(method))


    @run_only_when_suite_exist
    def stopTest(self, test):

        # if we running in multiprocess mode we should trigger suite closing
        # each time when we exiting test
        if self.options.multiprocess_workers:
            self.allure.impl.stop_suite()
            self.test_suite = False

    @run_only_when_suite_exist
    def stopContext(self, context):

        # if we running not in multiprocess mode we should trigger suite
        # closing only when exiting context
        if not self.options.multiprocess_workers and self.test_suite and \
                isinstance(context, ModuleType):
            self.allure.impl.stop_suite()
            self.test_suite = False

    @run_only_when_suite_exist
    def addError(self, test, err):
        message, trace = self._parse_tb(err)
        self.allure.impl.stop_case(Status.BROKEN, message=message, trace=trace)

    @run_only_when_suite_exist
    def addFailure(self, test, err):
        message, trace = self._parse_tb(err)
        if hasattr(test.test, "test"):
            method = test.test.test

        else:
            method = getattr(test.test, test.test._testMethodName)
        hierarchy = ".".join(filter(None, test.address()[1:]))
        description=self._getCapturedStdout()
        self.allure.impl.stop_case(Status.FAILED, message=message, trace=trace,description='描述:'+method.__doc__+'          输出:'+description)

    @run_only_when_suite_exist
    def addSuccess(self, test):
        if hasattr(test.test, "test"):
            method = test.test.test

        else:
            method = getattr(test.test, test.test._testMethodName)
        hierarchy = ".".join(filter(None, test.address()[1:]))
        description=self._getCapturedStdout()
        self.allure.impl.stop_case(Status.PASSED,description='描述:'+method.__doc__+'          输出:'+description)

    @run_only_when_suite_exist
    def addSkip(self, test):
        self.allure.impl.stop_case(Status.CANCELED)

    def finalize(self, result):
        self.allure.impl.store_environment()

    @staticmethod
    def _parse_tb(trace):
        # message = ''.join(
        #     traceback.format_exception_only(trace[0], trace[1])).strip()
        # trace = ''.join(traceback.format_exception(*trace)).strip()
        # return message, trace
        exc_type, exc_val, tb = trace
        # message = exc_message(trace)
        message = ''.join(
             traceback.format_exception_only(exc_type, exc_val if isinstance(exc_val, exc_type) else exc_type(exc_val))).strip()
        trace = ''.join(traceback.format_exception(
            exc_type,
            exc_val if isinstance(exc_val, exc_type) else exc_type(exc_val),
            tb
        ))
        return message, trace

def exc_message(exc_info):
    """Return the exception's message."""
    exc = exc_info[1]
    if exc is None:
        # str exception
        result = exc_info[0]
    else:
        try:
            result = str(exc)
        except UnicodeEncodeError:
            try:
                result = unicode(exc)  # flake8: noqa
            except UnicodeError:
                # Fallback to args as neither str nor
                # unicode(Exception(u'\xe6')) work in Python < 2.6
                result = exc.args[0]
    return result

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值