前面有聊过什么才是真正的自动化平台;其实说起来也是每个测试人的工作之路,从入门的测试执行、测试用例设计、自动化脚本开发、自动化架构开发、自动化平台开发,实际上我们大多数测试人都在纠结第一步的测试执行和第三步的自动化脚本开发,而领导层则更加着重看到平台化的产品;但在我看来测试用例设计和自动化平台开发则尤为重要。今天我们来介绍下实现平台化开发车载网络测试的基础内容 -- 如何通过Python调用CANoe工程,并在CANoe工程中添加TestModule和vTESTstudio工程,后面还会出一块进阶版的Python调用,本篇仅讲解基础的调用整块TestModule和vTESTstudio工程并启动测试。
Vector提供源码
import time, os, msvcrt
from win32com.client import *
from win32com.client.connect import *def DoEvents():pythoncom.PumpWaitingMessages()time.sleep(.1)
def DoEventsUntil(cond):while not cond():DoEvents()class CanoeSync(object):"""Wrapper class for CANoe Application object"""Started = FalseStopped = FalseConfigPath = ""def __init__(self):app = DispatchEx('CANoe.Application') app.Configuration.Modified = Falsever = app.Versionprint('Loaded CANoe version ', ver.major, '.', ver.minor, '.', ver.Build, '...', sep='')self.App = appself.Measurement = app.Measurement self.Running = lambda : self.Measurement.Runningself.WaitForStart = lambda: DoEventsUntil(lambda: CanoeSync.Started)self.WaitForStop = lambda: DoEventsUntil(lambda: CanoeSync.Stopped)WithEvents(self.App.Measurement, CanoeMeasurementEvents)def Load(self, cfgPath):# current dir must point to the script filecfg = os.path.join(os.curdir, cfgPath)cfg = os.path.abspath(cfg)print('Opening: ', cfg)self.ConfigPath = os.path.dirname(cfg)self.Configuration = self.App.Configurationself.App.Open(cfg)def LoadTestSetup(self, testsetup):self.TestSetup = self.App.Configuration.TestSetuppath = os.path.join(self.ConfigPath, testsetup)testenv = self.TestSetup.TestEnvironments.Add(path)testenv = CastTo(testenv, "ITestEnvironment2")# TestModules property to access the test modulesself.TestModules = []self.TraverseTestItem(testenv, lambda tm: self.TestModules.append(CanoeTestModule(tm)))def LoadTestConfiguration(self, testcfgname, testunits):""" Adds a test configuration and initialize it with a list of existing test units """tc = self.App.Configuration.TestConfigurations.Add()tc.Name = testcfgnametus = CastTo(tc.TestUnits, "ITestUnits2")for tu in testunits:tus.Add(tu)# TestConfigs property to access the test configurationself.TestConfigs = [CanoeTestConfiguration(tc)]def Start(self): if not self.Running():self.Measurement.Start()self.WaitForStart()def Stop(self):if self.Running():self.Measurement.Stop()self.WaitForStop()def RunTestModules(self):""" starts all test modules and waits for all of them to finish"""# start all test modulesfor tm in self.TestModules:tm.Start()# wait for test modules to stopwhile not all([not tm.Enabled or tm.IsDone() for tm in app.TestModules]):DoEvents()def RunTestConfigs(self):""" starts all test configurations and waits for all of them to finish"""# start all test configurationsfor tc in self.TestConfigs:tc.Start()# wait for test modules to stopwhile not all([not tc.Enabled or tc.IsDone() for tc in app.TestConfigs]):DoEvents()def TraverseTestItem(self, parent, testf):for test in parent.TestModules: testf(test)for folder in parent.Folders: found = self.TraverseTestItem(folder, testf)class CanoeMeasurementEvents(object):"""Handler for CANoe measurement events"""def OnStart(self): CanoeSync.Started = TrueCanoeSync.Stopped = Falseprint("< measurement started >")def OnStop(self) : CanoeSync.Started = FalseCanoeSync.Stopped = Trueprint("< measurement stopped >")class CanoeTestModule:"""Wrapper class for CANoe TestModule object"""def __init__(self, tm):self.tm = tmself.Events = DispatchWithEvents(tm, CanoeTestEvents)self.Name = tm.Nameself.IsDone = lambda: self.Events.stoppedself.Enabled = tm.Enableddef Start(self):if self.tm.Enabled:self.tm.Start()self.Events.WaitForStart()class CanoeTestConfiguration:"""Wrapper class for a CANoe Test Configuration object"""def __init__(self, tc): self.tc = tcself.Name = tc.Nameself.Events = DispatchWithEvents(tc, CanoeTestEvents)self.IsDone = lambda: self.Events.stoppedself.Enabled = tc.Enableddef Start(self):if self.tc.Enabled:self.tc.Start()self.Events.WaitForStart()class CanoeTestEvents:"""Utility class to handle the test events"""def __init__(self):self.started = Falseself.stopped = Falseself.WaitForStart = lambda: DoEventsUntil(lambda: self.started)self.WaitForStop = lambda: DoEventsUntil(lambda: self.stopped)def OnStart(self):self.started = Trueself.stopped = False print("<", self.Name, " started >")def OnStop(self, reason):self.started = Falseself.stopped = True print("<", self.Name, " stopped >")# -----------------------------------------------------------------------------
# main
# -----------------------------------------------------------------------------
打开一个空白的CANoe工程
app = CanoeSync()# 导入工程配置cfg文件
app.Load('CANoeConfig\PythonBasicEmpty.cfg')# 导入test modules的配置文件
app.LoadTestSetup('TestEnvironments\Test Environment.tse')# 导入configuration和test units文件
app.LoadTestConfiguration('TestConfiguration', ['TestConfiguration\EasyTest\EasyTest.vtuexe'])# 启动CANoe
app.Start() # 启动test modules
app.RunTestModules()# 启动TestConfiguration
app.RunTestConfigs()# wait for a keypress to end the program
print("Press any key to exit ...")
while not msvcrt.kbhit():DoEvents()# 关闭CANoe软件
app.Stop()
一、打开CANoe软件
app = CanoeSync()
注意这里打开的是所在电脑本机安装的CANoe软件,如果你的电脑存在多个CANoe版本,那么你就会打开最后安装的CANoe软件版本或者最后注册的CANoe的软件版本,如果你想要打开指定的软件版本的话,那么你就需要在执行该步骤之前对想要打开的CANoe软件版本进行注册,然后再执行该步骤,你就可以打开想要的版本了。这里肯定会有朋友想问怎么注册,下面听我一一道来。
好吧,其实很简单,查找RegisterComponents.exe文件所在的位置,查找到对应软件版本下的RegisterComponents.exe文件,然后以管理员身份运行即可;如果你问我这个不好搜怎么办,好说,推荐给你everything搜索工具;如果你有外网权限,直接使用谷歌浏览器搜索即可;如果没有VPN的话,那我就给你一个好了,直接去我的下载里面下载即可,免费开源,非常值得推荐。
如果你是未接触Python的小白,我也就不说太多,能够让你用起来最为关键,这里主要就有以下几个点:
将要打开的CANoe软件版本设置
获取到已打开CANoe的句柄;如果你不知道句柄是什么意思,你就把这个句柄当成你打开的CANoe即可,剩下的操作你就通过这个句柄进行。
二、导入你自己的CANoe工程配置
由于上一步打开的是一个空白的CANoe工程,距离你想要用例还有非常远的一段路要走,为了节省时间,最好就是保存好配置,直接导入即可(当然如果你希望挑战下自己也是可以通过自动化来实现配置)。
app.Load('D:\Work_Project\Automation\CANoe_Project\CANoeConfig\CANoe\Diagnostic.cfg')
demo中使用的是相对路径,但是个人建议使用绝对路径,这个通过Python也是很容易实现的,而且不容易出错,下面是给了获取路径的几种常用的方法,大家有需要可以自取。
import os
#得到当前工作目录
current_path = os.getcwd()
#返回指定目录下的所有文件和目录名
os.listdir()
# 上级目录
os.path.dirname(os.path.abspath(__file__))
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
获取路径下test.txt的路径
txt_path = os.path.join(os.path.dirname(cur_dir), "test.txt")
执行结果如下:
注意:如果需要导入自动化脚本TestModule或者vTESTstudio,要保证Test Setup里面为空,下面我会说为什么
三、导入要执行的自动化配置
这里要说下,为什么不直接把自动化配置保存在第二步打开的工程中呢?
这是个好问题啊,如果你在第二步直接打开了保存了自动化(无论是TestModule或vTESTstudio编译的文件),那你如何去获取到启动按钮呢?这个demo中可没有啊,并且,如果导入已存在的脚本执行文件,会报错的,因此要在上面那步保证配置为空,为了自动化配置做准备。如果你想在第二步保存配置也不是不行,请关注我后面的文章会有对应的讲解的。
# 导入test modules的配置文件
app.LoadTestSetup('TestEnvironments\Test Environment.tse')# 导入configuration和test units文件
app.LoadTestConfiguration('TestConfiguration', ['TestConfiguration\EasyTest\EasyTest.vtuexe'])
# 这里可以同时在TestConfiguration下一次性导入多个.vtuexe文件
# 这里的.vtuexe文件路径还是建议使用绝对路径(相对路径很大可能会搞到绝望)
app.LoadTestConfiguration('TestConfiguration', ['TestConfiguration\EasyTest\EasyTest.vtuexe'],['TestConfiguration\EasyTest\EasyTest1.vtuexe'], ['TestConfiguration\EasyTest\EasyTest2.vtuexe'])
大家可以在这里看到一个是导入test modules,一个是导入configuration和test units文件;他们有什么区别呢?test module是在CANoe软件中的test module节点,然后通过CAPL实现的简单自动化测试脚本;而configuration名称和test units文件是通过vTESTstudio软件开发的自动化测试脚本编译生成的后缀名为.vtuexe的文件;这是vector工具链中两种完全不同的自动化脚本(虽然都是通过CAPL实现,但是平台和使用方法完全不同,我的其他文章中有介绍,有兴趣的话可以找下);因此上面两种配置不要出现在同一个CANoe工程中,也就是说,两种导入配置一次只能使用其中的一个(还是那句话同时导入2个也不是不行,但是为了能够跑起来,建议不要这样做)。
四、启动CANoe
为什么要单独说呢?因为对于CANoe工程的自动化来说,启动CANoe这是将CANoe这个软件运行起来,并没有运行自动化脚本;就是左上角的黄色闪电,我这个由于没有license都是灰色的大家心理把它当成黄色的就行;箭头很大,大家应该能理解了。
# 启动CANoe
app.Start()
五、启动测试模块
到了这里才是我们真正需要的执行的脚本,同第三步一样在这里也是有2种,要保持跟第三步加入的一致,如果在第三步你使用的是test modules那你就使用app.RunTestModules();如果你加入的是TestConfiguration,那就使用app.RunTestConfigs();在执行完该步骤后,你就真正的能够看到你的自动化脚本跑起来了。
这里启动我们就能看到脚本框黄色的被启动了,也是很大的箭头!!!
# 启动test modules
app.RunTestModules()# 启动TestConfiguration
app.RunTestConfigs()
注意:一定要跟第三步加入的脚本配置同步,只选择其中的一种。
六、执行完成,关闭CANoe软件
这里我们依然使用demo中的,等待运行结束(不过这里需要某些配置,建议直接使用time.sleep,更加好用点,如果真的想实时监测测试执行状态,那就关注后面的文章吧!!!),然后关闭CANoe软件。
# wait for a keypress to end the program
print("Press any key to exit ...")
while not msvcrt.kbhit():DoEvents()# 关闭CANoe软件
app.Stop()
如果你想更多的操作比如:删除dbc、添加dbc、控制多个TestConfiguration、控制TestUnit、控制TestGroup、设置控制TestCase、自动配置执行脚本、执行项目等等,只要你能想到的都可以实现,都会在后面的文章一一为大家呈现更高阶的玩法,这个我也在文章什么是是真正的自动化有聊过,都是可以实现的;当然如果你想合作,也欢迎联系我!!!