pytest+allure+excel实现python调CANoe进行诊断自动化测试(二)

一、前言

本文主要介绍整体框架构成及python调CANoe COM接口封装的方法

二、测试框架的好处

当我们开发软件时,需要确保软件的各个部分能正常工作。为了做到这一点,我们需要测试软件,即检查软件是否按照我们预期的方式运行。传统的测试通常是手动的,即人工点击按钮、输入数据来模拟用户行为,来检查软件功能是否正常。但是这种方式效率低下且容易出错。
为了提高测试效率和质量,我们可以构建一个自动化测试框架。这个框架就像一个机器人,能自动执行测试用例,验证软件的功能是否正常。构建自动化测试框架的好处是可以节省时间和人力资源,并确保每次更新后软件依然正常运行。同时,自动化测试框架可以帮助我们更方便地合作,共享测试用例和结果,确保软件质量。因此,构建自动化测试框架可以帮助我们更快速、更准确地测试软件,确保软件质量。

主要好处分为以下几点:

1. 提高测试效率:自动化测试框架可以自动执行测试用例,省去了手动测试的时间和劳动成本。测试团队可以更快速地执行测试,并及时发现问题。
2. 提高测试覆盖率:自动化测试框架可以帮助测试团队更全面地覆盖功能、边界条件和各种场景,确保软件质量。
3. 降低测试成本:虽然构建自动化测试框架需要一定的投入,但长期来看,自动化测试可以减少重复劳动和人力资源成本。同时,自动化测试框架可以更快速地应对软件变更,减少因人为错误导致的质量问题。
4. 提高持续集成与交付软件的速度:自动化测试框架可以与持续集成工具(如 Jenkins)和持续交付流程结合,确保每次代码提交都能进行自动化测试,从而提高软件交付速度和质量。
5. 方便协作与回归测试:构建自动化测试框架可以使测试团队更容易共享测试用例和测试结果,方便团队合作。同时,自动化测试框架可以帮助团队更方便地进行回归测试,确保每次更新后软件依然符合预期。

三、框架构成

从上往下依次介绍:

allure-report

主要存放生成完的allure测试报告,之后执行结束后打开index.html即可查看完整的测试报告

comm

存放公用方法,因为是自动化测试框架嘛,后面后其他自动化测试要实现时需要用到的公用方法都在此处存放使用

canoe_application.py主要是python调CANoe的方法封装

execute_excel.py主要是读写excel的方法

security_key.py主要是进行安全解锁的方法

log

用于存放日志,目前只存放了CANoe生成的trace日志

report

存放allure生成的json文件。

Allure生成的.json文件是一种用于存储测试结果数据的格式化文件。通常,当运行测试并生成Allure测试报告时,Allure会生成.json文件来存储测试执行的详细信息,包括测试用例的执行结果、测试用例的状态、测试步骤的日志、附加信息等

里面的environment.properties是allure报告的配置文件

testcase

存放的是测试脚本。

testdata

存放测试数据。

根目录下的文件

config.ini:配置路径、设备等

environment.properties:测试报告环境配置,方便复制到allure生成的json文件中

main.py:执行文件

四、python调CANoe COM封装

canoe_application.py

import os
import time
from win32com.client import *
from pathlib import Path

class CANoe:
    def __init__(self):
        # self.application = None
        self.application = DispatchEx("CANoe.Application")
        self.ver = self.application.Version
        print('Loaded CANoe version', self.ver.major, '.', self.ver.minor, '.', self.ver.Build, '...')
        self.logging_collection = None
        self.logger = None
        self.log_target_dir = None
        self.Measurement = self.application.Measurement.Running

    def open_cfg(self, cfgname):
        """
        打开工程
        :param cfgname:
        :return:
        """
        if(self.application != None):
            if os.path.isfile(cfgname) and (os.path.splitext(cfgname)[1] == '.cfg'):
                self.application.Open(cfgname)
                print("opening..."+cfgname)
            else:
                raise NameError("Can't find CANoe cfg file")
        else:
            raise RuntimeError("CANoe Application is missing, unable to open simulation")


    def close_cfg(self):
        """
        关闭工程
        :return:
        """
        if(self.application != None):
            self.application.Quit()
            self.application = None

    def start_Measurement(self):
        """
        运行
        :return:
        """
        retry = 0
        retry_counter = 5  # 尝试5次
        while not self.application.Measurement.Running and (retry < retry_counter):
            self.application.Measurement.Start()
            time.sleep(1)
            retry += 1
        if(retry == retry_counter):
            raise RuntimeWarning("CANoe start measurement failed, Please Check Connection!")

    def stop_Measurement(self):
        """
        停止
        :return:
        """
        if self.application.Measurement.Running:
            self.application.Measurement.Stop()
        else:
            pass

    def send_Diag_Request(self, qualifier, stream_flag=False):
        """
        发送诊断请求
        :param qualifier: 限定符
        :param stream_flag: 是否是给定的限定符
        :return:
        """
        data = qualifier
        my_bytearray = bytearray.fromhex(data)
        qualifier_list = list(my_bytearray)
        self._diag_Request(qualifier_list, stream_flag)

        # self.DiagDev = self.application.Networks("CAN").Devices("EPS").Diagnostic
        # self.DiagReq = self.DiagDev.CreateRequest('0x10 0x01')
        # self.DiagReq.Send()
        # self.Diag.DiagStartTesterPresent()

    def init_service(self, network, device):
        """
        建立诊断服务
        :param network: 网段
        :param device: ECU qualifier
        :return:
        """
        time.sleep(10)
        self.__gDiag = self.application.Networks(network).Devices(device).Diagnostic
        # self.__gDiag.DiagStartTesterPresent()


    def start_TesterPresent(self):
        """
        开启诊断仪在线
        :return:
        """

        self.__gDiag.DiagStartTesterPresent()

    def stop_TesterPresent(self):
        """
        停止诊断仪在线
        :return:
        """
        self.__gDiag.DiagStopTesterPresent()

    def _diag_Request(self, qualifier, stream_flag=False):
        """
        执行服务
        :param qualifier:
        :param stream_flag: True以字节流形式发送,False根据具体限定符发送
        :return:
        """

        global diag_request
        if stream_flag:
            pass
        else:
            for a in qualifier:
                print('%x' % a, end=' ')
        if not stream_flag:
            diag_request = self.__gDiag.CreateRequest(qualifier)
        else:
            diag_request = self.__gDiag.CreateRequestFromStream(bytearray(qualifier))
            # else:
            #     diag_request = self.__gDiag.SetComplexParameter(bytearray(qualifier), iteration, subParameter)

        diag_request.Send()

    def check_Diag_Response(self):
        return self._diag_Response()

    def check_positive(self):
        global diag_request
        Res = ''
        while diag_request.Pending:
            time.sleep(1)
        if diag_request.Responses.Count == 0:
            Res = "NO RESPONSE"
        else:
            for k in range(1, diag_request.Responses.Count + 1):
                diag_resp = diag_request.Responses(k)
                stream = diag_resp.Stream
            if diag_resp.Positive:
                return True
            else:
                return False

    def _diag_Response(self):
        """
        诊断响应
        :return:
        """
        global diag_request
        Res = ''
        positive = True
        while diag_request.Pending:
            time.sleep(1)
        if diag_request.Responses.Count == 0:
            Res = "NO RESPONSE"
        else:
            for k in range(1, diag_request.Responses.Count + 1):
                diag_resp = diag_request.Responses(k)
                stream = diag_resp.Stream
            if diag_resp.Positive:
                positive = True
            else:
                positive = False

            for i in stream:
                Res = Res + '{:02X} '.format(i)
        return Res, positive


    def init_logging_collection(self):
        """

        :return:
        """
        self.logging_collection = self.application.Configuration.OnlineSetup.LoggingCollection

    def select_logger(self, logger_number:int = None):
        """

        :param logger_number:
        :return:
        """
        self.logger = self.logging_collection.Item(logger_number)

    def set_log_target_dir(self, abs_path_to_dir = None):
        """

        :param abs_path_to_dir: log存放位置
        :return:
        """
        path = Path(abs_path_to_dir)
        if path.exists():
            self.log_target_dir = path
        else:
            raise Exception("CANoe Wrapper: Log target directory does not exist")

    def set_logfile_name(self, file_name:str = None):
        """

        设置日志名字
        :param file_name:
        :return:
        """
        file = Path(file_name)
        if file.suffix == '.blf':
            full_path = Path.joinpath(self.log_target_dir, file)
            self.logger.fullName = str(full_path)
        else:
            raise Exception("CANoe Wrapper: Log target file has incorrect extension")

    def start_logging(self):
        """
        开始记录日志
        :return:
        """
        if self.logger is not None:
            self.logger.Trigger.Start()
        else:
            raise Exception("CANoe Wrapper: No active logger was set")

    def stop_logging(self):
        """
        停止记录日志
        :return:
        """
        if self.logger is not None:
            self.logger.Trigger.Stop()
        else:
            raise Exception("CANoe Wrapper: No active logger was set")

    def get_logger_status(self):
        """
        获取日志状态
        :return: 返回TRUE 表示log状态为running,返回FALSE 表示log状态为stopped
        """
        if self.logger is not None:
            return str(self.logger.Trigger.Active)

    def sleep(self, ms:int):
        time.sleep(ms/1000)



if __name__ == '__main__':
    app = CANoe()
    app.open_cfg(r"D:\work\Partial_SDA\workspace\canoeworkspace\ECUTestwork.cfg")
    time.sleep(1)

CANoe的COM接口在帮助文档中可以找到:

剩下的代码通过资源上传了,自行获取 python调CANoe/zlgcan实现诊断测试资源-优快云文库

<think>嗯,用户想了解如何使用CANoe进行CAN总线的自动化测试配置与实现方法。首先,我需要回顾一下CANoe的基本功能和自动化测试相关的模块。根据引用[2],CANoe支持多种协议,包括CAN、LIN、FlexRay和以太网,而且具备自动化测试的能力。用户可能需要具体的步骤或方法,所以得把不同的配置方式整理清楚。 引用[4]和[5]提到了两种配置方式:CAPL编程和Panel接口方式。CAPL是CANoe内置的脚本语言,适合处理复杂逻辑,而Panel接口方式可能更适合结合图形界面操作。需要明确这两种方法的具体步骤、优缺点以及适用场景。另外,引用[3]提到了Test Step功能,这也是自动化测试的一部分,可能需要补充进去。 用户可能对如何选择不同的方法感到困惑,所以需要比较它们的异同,并给出建议。例如,CAPL适合有编程经验的用户,而Panel方式可能更直观。同时,自动化测试通常需要结合测试框架,比如引用[2]提到的pytest+allure+excel,可能需要简要说明如何集成。 另外,代码示例是必要的,用户可能需要看到具体的CAPL脚本或Panel配置示例。确保代码格式正确,并解释关键部分。比如,在CAPL中如何发送CAN消息,如何定义测试用例的结构。还要注意引用标识的正确添加,如[^3]或[^4]。 还需要考虑用户的实际应用场景,比如是用于诊断测试还是性能测试。引用[5]中提到适用场景的分析,这部分需要总结到回答中,帮助用户根据需求选择合适的方法。 最后,检查是否有遗漏的配置方式,比如SystemVariables数组或其他方法,但根据引用[1],可能这些内容在另一篇未详细引用的文章中,当前提供的引用可能不足以涵盖,所以暂时专注于CAPL、Panel和Test Step。 总结步骤应该是先介绍CAPL编程方式,再讲Panel接口,然后Test Step,最后集成外部框架。每个部分包括实现步骤、代码示例、特点和适用场景。确保语言简洁,结构清晰,符合用户要求的层次。</think>### CANoe的CAN总线自动化测试配置与实现方法 #### 一、CAPL编程方式(核心方法) 1. **实现步骤** - 在CANoe工程中创建`Test Module`,编写CAPL脚本定义测试逻辑,例如发送CAN报文、校验响应数据等。 - 使用`testcase`关键字定义测试用例,通过`if-else`或`while`实现条件判断和循环控制。 - 用`TestStepPass()`和`TestStepFail()`标记测试结果。 2. **示例代码** ```c testcase CheckEngineSpeed() { CANMessage msg; msg.id = 0x100; // CAN ID msg.dlc = 8; msg.byte(0) = 0x50; // 示例数据 output(msg); delay(100); // 等待响应 if (sysGetVariable("EngineSpeed") == 1500) { TestStepPass("Engine speed valid"); } else { TestStepFail("Engine speed invalid"); } } ``` 3. **特点与场景** - **优点**:灵活性强,适合复杂逻辑测试(如诊断协议UDS)[^4]。 - **缺点**:需要CAPL编程基础。 - **适用场景**:需动态控制总线通信的测试(如故障注入、压力测试)。 #### 、Panel接口方式(图形化操作) 1. **实现步骤** - 在Panel Designer中创建控件(按钮、输入框等),绑定系统变量或CAN信号[^5]。 - 通过`on click`事件触发测试动作,例如发送特定CAN报文或读取信号值。 - 结合Test Feature记录测试步骤。 2. **示例配置** ```c on sysvar_update MyPanel::Button1 { if (@sysvar::Button1 == 1) { write("Triggering test..."); @sysvar::TestResult = 1; // 更新测试状态 } } ``` 3. **特点与场景** - **优点**:操作直观,适合快速搭建简单测试流程[^5]。 - **缺点**:逻辑扩展性较弱。 - **适用场景**:手动测试转自动化、功能验收测试。 #### 三、Test Feature模块(标准化测试) 1. **实现步骤** - 使用Test Configuration Wizard定义测试序列,设置预置条件(如关闭ECU休眠)[^3]。 - 通过Test Step Editor添加断言(Assertion),例如检查CAN信号范围是否满足$0 < Speed < 200$。 - 生成HTML/XML格式的测试报告。 2. **特点与场景** - **优点**:标准化程度高,支持批量测试用例管理。 - **适用场景**:合规性测试(如ISO 14229诊断协议验证)。 #### 四、集成外部框架(高级扩展) 1. **Python+pytest集成** - 通过COM接口CANoe API,实现跨平台测试(示例代码)[^2]: ```python import win32com.client canoe_app = win32com.client.Dispatch("CANoe.Application") test_module = canoe_app.Configuration.TestModules.Item("MyTest") test_module.Start() ``` 2. **Excel数据驱动** - 将测试用例参数存储在Excel中,通过CAPL或Python读取并动态执行。 --- ### 配置方式对比分析 | 方法 | 开发效率 | 灵活性 | 学习成本 | 适用阶段 | |---------------|----------|--------|----------|----------------| | CAPL编程 | 中 | 高 | 高 | 开发/系统测试 | | Panel接口 | 高 | 低 | 低 | 验收测试 | | Test Feature | 中 | 中 | 中 | 合规测试 | | 外部框架集成 | 低 | 极高 | 极高 | 持续集成(CI/CD)| ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值