zoukankan      html  css  js  c++  java
  • QC API全系列揭秘之Test Execution操作(全网首发)

    (原创文章,转载请注明出处。)

    一、QC简介:

            Quality Center存在至今已经走过了10多个年头,名字从一开始的TD,到后来的QC,再到现在的ALM。所属公司从开始的Mercury到现在的HP,核心一直没变,变的只有名字。随着Mercury最核心的高层、架构师和专家的离开,现在每每的升级都带来诸多失望,再也没有当初使用Mercury工具的时候那样心潮澎湃,看看QC,看看QTP,不多言语。如果能够坚持做好的话,现在哪有TestLink、哪有禅道什么事。然而,QC框架的设计核心,拿到现在来看,依然是测试管理框架的主流。QC设计思路简单清晰,从测试需求到测试用例,再到执行测试用例、提交缺陷、跟踪缺陷,整个过程清晰且易于理解,时至今日,依然被广泛沿用。(微信公众号“诗泽园”)

    二、写作目的:

            写此系列的目的,不是为了情怀,而是为了将QC接口的调用方式整理成册。一来是为了通过对QC接口调用的理解,去更深入的理解测试框架的概念(当然仅仅包含小部分);二来是为了通过二次开发,解决QC使用过程中的诸多不便。

    三、解决问题:

    QC当前存在以下问题:

    1、需求或用例的导入导出依然不完善。之前就写过一个工具解决这个问题:https://download.csdn.net/download/yoyoalphax/4441588,但近期依然常有人催促我更新版本,之后会专门发一篇关于用例树解析的图文。(网上资料大部分需要admin账户通过后台SQL做关键字筛选获得结果,但事实上与实际的导入导出过程有所出入。)

    2、用例执行顺序需要参考已有用例集,无法随心所欲变化。

    3、每次执行的用例集列表无法保存并复用。

    4、手动执行前需要处理解锁等额外步骤。

    5、测试结果随有统一展示,但需要人工收集结果等等。

    四、本文重点:
            本文着重介绍Test Execution部分,解决了以上罗列的后几个问题,并为某些问题提供解决的条件。其余需求树、用例树读取及写入部分以后再介绍。
            Test Execution属于自动化测试框架的一部分,我们先从框架说起。
            框架的概念:
            测试框架的话题,范围实在是太大了,我们还是围绕QC来说。我们仅仅考虑从测试用例的编写、测试用例的执行和测试报告来看。用过的同学都清楚,TestPlan里可以存放测试用例,而TestLab里可以建TestSet并形成用例集并且执行,Report里查看用例,这就是基本流程。而对于自动化测试的流程而言,用QC的方式又有所区别:首先,是测试工具的关联。QC需要安装QTP或LR的插件,使得QC的测试执行模块里可以识别这两类代码。又或者你用的是其他第三方工具或用JAVA和.Net自开发的测试工具,那你需要用VB6编写关联脚本,使得QC能够调用你写的代码,这个过程我们不在本文中讨论。其次,是测试脚本的存储。以用例的形式存储在TestPlan里,最终落到QC的后台SQL数据库里,并能实现脚本与数据的分开存储。再者是测试用例集的构成,这部分是放在TestLab里去管理。按照业务逻辑,将已有用例归集并排序,形成业务逻辑并保存。最后是测试执行,按照被测系统版本、范围,选择相应的业务节点去触发执行,获得结果。

            这个过程其实分两个阶段,一个是测试开发阶段,另一个是测试执行阶段,两个阶段各有各的自动化设计方面的考虑。这个不是本文的主旨,但是我也顺便捋一下,加深理解。测试开发阶段,其实是要设计出狭义的测试框架,也就是一个可以团队合作开发的测试脚本的模式,包含底层库、业务的封装、上层调用及断言等等。有了狭义的框架后,需要有偏业务的测试人员介入,将测试脚本归集形成测试集。在测试开发阶段,往往这两块是一同进行的,边改边拼接。测试Q执行阶段,其实是要有测试执行框架去支撑的,尤其是有大批量的测试脚本和测试机需要团队去匹配执行时,这个框架就显得尤为重要。其中涉及到的关键点,如待测范围的选择及保存、测试机的管理(vmware or docker)、用例执行时的动态分配、异常处理、报告收集等等。

            本文所涉及的内容应该是通过QC实现自动化测试脚本运行的前提下,实现测试范围的选择与保存,测试脚本的自动化执行并做后续的报告收集工作。

    五、QC接口规范:
            具体的接口规范你可以尝试通过百度查询“QC OTA”或者“QC对象模型”,获得接口说明及使用规范。但以下的核心代码均是本人键盘手打敲击而成,尤其是核心的业务树生成及测试执行部分,均为首次发布。还望转载或代码复用时注明出处。(出自微信公众号“诗泽园”或博客园“朝花夕拾”--https://www.cnblogs.com/alphaxu/)

    六、QC接口操作Test Execution:

    正式切入正题:

    定义全局变量

    真实代码中有很多控制类及展示类,都已经去除了。这里只展示核心代码。

    1        TDConnection tdc = new TDConnection();
    2        TDAPIOLELib.TSScheduler QCscheduler;
    3        //用于最终真实启动用例及监测执行结果
    4        TDAPIOLELib.ExecutionStatus QCexecutionStatus;
    5        //用于反馈执行结果及做结果的动态刷新
    6        List QClistForTSTest;

    QC服务器连接、登录(身份验证)及项目连接

    服务器连接为初始化连接,好比你刚登录QC终端时它给你的反馈。一般会碰到要你reload ActiveX或者OTA初始化失败之类的错误。处理方法是把QC缓存文件夹删除,再访问,让其重新reload。这块代码里会有处理,但这类代码就不贴了。

     1        //初始化连接
    2        private void InitConnect(string serverName)
    3        
    {
    4            try
    5            {
    6                if ((tdc.Connected == false) || (tdc.Connected == true && tdc.ServerName != serverName))
    7                    tdc.InitConnectionEx(serverName);
    8            }
    9            catch (Exception ex)
    10            {
    11                Console.WriteLine(ex.ToString());
    12                MessageBox.Show("Server Error""Warning");
    13            }
    14        }
    15
    16        //身份验证
    17        private void GetAuthenticate(string userName, string passWord)
    18        
    {
    19            try
    20            {
    21                tdc.Login(userName, passWord);
    22                TDAPIOLELib.List projectList = tdc.get_VisibleProjects(tdc.VisibleDomains[1].ToString());
    23                //VisibleDomains[1]为项目列表中的第一个Domain,多的话自行处理
    24                for (int i = 1; i <= projectList.Count; i++)
    25                {
    26                    projectsBox.Items.Add(projectList[i]);
    27                    //将Domain下所有项目读取出来,以备后用
    28                }            
    29            }
    30            catch (Exception ex)
    31            {
    32                Console.WriteLine(ex.ToString());
    33                MessageBox.Show("Please check the User Name is correct or not.""Warning");
    34            }
    35        }
     1        //项目连接
    2        private void LoginButton_Click(object sender, EventArgs e)
    3        
    {
    4            try
    5            {
    6                string ProjectName = projectsBox.SelectedItem.ToString();
    7                tdc.Connect(tdc.VisibleDomains[1].ToString(), ProjectName);
    8
    9                //调用生成业务树代码,也可以不在此处调用
    10                Thread td_tree = new Thread(new ThreadStart(this.CreateTreeView));
    11                td_tree.Start();
    12            }
    13            catch (Exception ex)
    14            {
    15                Console.WriteLine(ex.ToString());
    16                MessageBox.Show("Please choose the project.""Warning");
    17            }
    18        }

    重点之一:递归生成业务树

    同样需要新开线程调用,先生成根节点,再递归生成业务树

     1        //生成根节点并调用递归树
    2        private void CreateTreeView()
    3        
    {
    4            try
    5            {
    6                TreeNode mainNode = new TreeNode();
    7                mainNode.Name = "Root";
    8                mainNode.Text = "Root";
    9                Add_TreeRoot(mainNode);
    10
    11                SysTreeNode test_folder;
    12                TestSetFactory globalTestSetFactory;
    13                List l_List;
    14                TreeNode r_node = new TreeNode();
    15
    16                int nodeCount;
    17                nodeCount = qcProjectTree.GetNodeCount(true);
    18                TreeNode[] r_nodeArray = new TreeNode[10000];
    19                r_nodeArray = qcProjectTree.Nodes.Find("Root"false);
    20                r_node = r_nodeArray[0];
    21
    22                TestSetTreeManager tm;
    23                tm = (TDAPIOLELib.TestSetTreeManager)tdc.TestSetTreeManager;
    24                test_folder = (TDAPIOLELib.SysTreeNode)tm.Root;
    25                globalTestSetFactory = (TDAPIOLELib.TestSetFactory)tdc.TestSetFactory;
    26                l_List = globalTestSetFactory.NewList("");
    27                recursiveTreeBuilder(test_folder, r_node);
    28            }
    29            catch (Exception ex)
    30            {
    31                Console.WriteLine(ex.ToString());
    32            }
    33        }
    34
    35        //递归生成业务树
    36        private bool recursiveTreeBuilder(TDAPIOLELib.SysTreeNode folder, TreeNode parent)
    37        
    {
    38            TDAPIOLELib.List folders, tests;
    39            TDAPIOLELib.TestSetFactory objTestSetFactory;
    40            TDAPIOLELib.TestSetFolder objTSFolder;
    41            TreeNode n;
    42            try
    43            {
    44                folders = folder.NewList();
    45                foreach (TDAPIOLELib.SysTreeNode f in folders)
    46                {
    47                    TreeNode nodeChild = new TreeNode();
    48                    nodeChild.Name = f.Name;
    49                    nodeChild.Text = f.Name;
    50                    nodeChild.ImageIndex = 0;
    51                    Add_TreeNode(parent, nodeChild);
    52                    n = parent.Nodes[nodeChild.Name];
    53                    recursiveTreeBuilder(f, n);
    54                }
    55
    56                objTSFolder = (TDAPIOLELib.TestSetFolder)folder;
    57                if (objTSFolder.NodeID != 0)
    58                {
    59                    objTestSetFactory = (TDAPIOLELib.TestSetFactory)objTSFolder.TestSetFactory;
    60                    tests = objTestSetFactory.NewList("");
    61                    foreach (TDAPIOLELib.TestSet testSet in tests)
    62                    {
    63                        TreeNode nodeChild1 = new TreeNode();
    64                        nodeChild1.Name = testSet.ID.ToString();
    65                        nodeChild1.Text = testSet.Name;
    66                        nodeChild1.ImageIndex = 1;
    67                        Add_TreeNode(parent, nodeChild1);
    68                        n = parent.Nodes[nodeChild1.Name];
    69                        n.Tag = objTSFolder.Path + @"" + testSet.Name;
    70                    }
    71                }
    72                return true;
    73            }
    74            catch (Exception ex)
    75            {
    76                Console.WriteLine(ex.ToString());
    77                return false;
    78            }
    79        }

    以下代码用委托的方式生成节点,保证在业务树生成过程中可随时点击并保证界面不出现假死(跟业务树生成无直接关系,可忽略)

     1        delegate void Add_Node(TreeNode parent, TreeNode node);
    2        private void Add_TreeNode(TreeNode parent, TreeNode node)
    3        
    {
    4            if (this.InvokeRequired)
    5            {
    6                this.BeginInvoke(new Add_Node(Add_TreeNode), parent, node);
    7            }
    8            else
    9            {
    10                parent.Nodes.Add(node);
    11            }
    12            Thread.Sleep(10);
    13        }

    生成业务树后,由用户通过业务树选择需要运行的节点,形成待测试列表,就是后续代码中的TestSetList,这部分代码跟QC无关,也不列举了。

    重点之二:测试执行

    先看一个总体调用RunTestSetPlan,当然也是需要新开线程调用的:

    1        Thread td_runTestSetPlan = new Thread(new ThreadStart(this.RunTestSetPlan));
    2        td_runTestSetPlan.Start();

    调用步骤是先检验validate,然后运行run,最后收集结果monitor:

     1        private void RunTestSetPlan()
    2        
    {
    3            try
    4            {
    5                if (tdc.ProjectConnected == true)
    6                {
    7                    if (TestSetNameList.Items.Count != 0)
    8                    {
    9                        for (int i = 0; i < TestSetList.Items.Count; i++)
    10                        {
    11                            if (validateTestSetID(TestSetList.Items[i].ToString(), i) == true)
    12                            {
    13                                if (runTestSet(TestSetList.Items[i].ToString(), i) == true)
    14                                {
    15                                    if (monitorTestSet(TestSetList.Items[i].ToString()) == true)
    16                                    {
    17                                        QCexecutionStatus.RefreshExecStatusInfo("all"true);                      
    18                                    }
    19                                }
    20                            }
    21                        }
    22                    }
    23                    else
    24                        MessageBox.Show("Empty Test Set List.""Warning");
    25                }
    26                else
    27                    MessageBox.Show("Connection Error, please login again.""Warning");
    28            }
    29            catch (Exception ex)
    30            {
    31                Console.WriteLine(ex.ToString());
    32            }
    33        }

    validate通过QCfilter,使用testSetID去做筛选,取得我们需要的测试集,然后根据判断测试集是否为空来确定测试集是否有效,代码如下:

     1        private bool validateTestSetID(string testSetID, int i)
    2        
    {
    3            TDAPIOLELib.TestSetFactory QCtestSetFactory = (TDAPIOLELib.TestSetFactory)tdc.TestSetFactory;
    4            TDAPIOLELib.TDFilter QCfilter = (TDAPIOLELib.TDFilter)QCtestSetFactory.Filter;
    5            QCfilter["CY_CYCLE_ID"] = testSetID;
    6            List QClist = QCfilter.NewList();
    7            if (QClist.Count != 0)
    8            {
    9                return true;
    10            }
    11            else
    12            {
    13                return false;
    14            }             
    15        }

    runTest与之前类似,获取首只测试集对象后,通过QCTSTestFactory将测试集下的所有用例形成QClistForTSTest列表,并用QCscheduler执行,代码如下:

     1        private bool runTestSet(string testSetID, int i)
    2        
    {
    3            try
    4            {
    5                TDAPIOLELib.TestSetFactory QCtestSetFactory = (TDAPIOLELib.TestSetFactory)tdc.TestSetFactory;
    6                TDAPIOLELib.TDFilter QCfilter = (TDAPIOLELib.TDFilter)QCtestSetFactory.Filter;
    7                QCfilter["CY_CYCLE_ID"] = testSetID;
    8                List QClist = QCfilter.NewList();
    9                TDAPIOLELib.TestSet QCtestSet = (TDAPIOLELib.TestSet)QClist[1];
    10                TDAPIOLELib.TestSetFolder QCtestSetFolder = (TDAPIOLELib.TestSetFolder)QCtestSet.TestSetFolder;
    11                TDAPIOLELib.TSTestFactory QCTSTestFactory = (TDAPIOLELib.TSTestFactory)QCtestSet.TSTestFactory;
    12                QClistForTSTest = QCTSTestFactory.NewList("");
    13                try
    14                {
    15                    string applicationCreationTime = File.GetCreationTime(@"The path of Your application").ToString();
    16                    string machineName = System.Net.Dns.GetHostEntry("IP address of test machine").HostName.Split('.')[0];
    17                    QCtestSet["CY_USER_01"] = applicationCreationTime;
    18                    Thread.Sleep(1000);
    19                    QCtestSet["CY_USER_02"] = machineName;
    20                    Thread.Sleep(1000);
    21                    QCtestSet.ResetTestSet(false);
    22                    Thread.Sleep(1000);
    23                    QCtestSet.Post();
    24                    Thread.Sleep(1000);
    25                    QCtestSet.Refresh();
    26
    27                    Thread.Sleep(10000);
    28
    29                    QCscheduler = (TDAPIOLELib.TSScheduler)QCtestSet.StartExecution("");
    30                    QCscheduler.Run(QClistForTSTest);
    31                    Thread.Sleep(5000);
    32
    33                    return true;
    34                }
    35                catch (Exception ex)
    36                {
    37                    Console.WriteLine(ex.ToString());
    38                    return false;
    39                }
    40            }
    41            catch (Exception ex)
    42            {
    43                Console.WriteLine(ex.ToString());
    44                return false;
    45            }
    46        }

    Monitor,使用QCTestExecStatus中的QCexecutionStatus作为计数器,逐个判断当前步骤是否跑完,汇总运行结果,代码如下:

     1        private bool monitorTestSet(string testSetID)
    2        
    {
    3            try
    4            {
    5                QCexecutionStatus = (TDAPIOLELib.ExecutionStatus)QCscheduler.ExecutionStatus;
    6                QCexecutionStatus.RefreshExecStatusInfo("all"true);
    7                TDAPIOLELib.TestExecStatus QCTestExecStatus;
    8                int checkStep = 1;
    9                while (checkStep <= QCexecutionStatus.Count)
    10                {
    11                    QCexecutionStatus.RefreshExecStatusInfo("all"true);
    12                    QCTestExecStatus = (TDAPIOLELib.TestExecStatus)QCexecutionStatus[checkStep];
    13                    if (QCTestExecStatus.Message == "Nothing" || QCTestExecStatus.Message == "Waiting..." || QCTestExecStatus.Message == "Connecting...")
    14                    {
    15                        Thread.Sleep(10000);
    16                        QCexecutionStatus.RefreshExecStatusInfo("all"true);
    17                    }
    18                    else
    19                    {
    20                        TDAPIOLELib.TSTest QCtestOfTestSet = (TDAPIOLELib.TSTest)QClistForTSTest[checkStep];
    21                        QCtestOfTestSet.Refresh();
    22                        switch (QCTestExecStatus.Message)
    23                        {
    24                            case "Completed":
    25                                if (QCtestOfTestSet.Status == "Passed")
    26                                    CurrentStateBar("Step " + checkStep + " " + QCtestOfTestSet.Name + " execution completed -> " + QCtestOfTestSet.Status);
    27                                else
    28                                    if (QCtestOfTestSet.Status == "Failed")
    29                                        CurrentStateBar("Step " + checkStep + " " + QCtestOfTestSet.Name + " execution completed -> " + QCtestOfTestSet.Status);
    30                                    else
    31                                        CurrentStateBar("Step " + checkStep + " " + QCtestOfTestSet.Name + " execution unknown -> " + QCtestOfTestSet.Status);
    32                                break;
    33                            case "No available hosts":
    34                                CurrentStateBar("Step " + checkStep + " " + QCtestOfTestSet.Name + " execution failed (No available hosts) -> " + QCtestOfTestSet.Status);
    35                                break;
    36                            case "Cannot get RemoteAgent's ClassID for test type <TestType>":
    37                                CurrentStateBar("Step " + checkStep + " " + QCtestOfTestSet.Name + " Cannot get RemoteAgent's ClassID for test type <TestType> -> " + QCtestOfTestSet.Status);
    38                                break;
    39                            case "Host connected":
    40                                CurrentStateBar("Step " + checkStep + " " + QCtestOfTestSet.Name + " execution failed (Host connected) -> " + QCtestOfTestSet.Status);
    41                                break;
    42                            default:
    43                                CurrentStateBar("Step " + checkStep + " " + QCtestOfTestSet.Name + " unhandled case -> " + QCtestOfTestSet.Status);
    44                                break;
    45                        }
    46                        checkStep = checkStep + 1;
    47                    }
    48                }
    49                return true;
    50            }
    51            catch (Exception ex)
    52            {
    53                Console.WriteLine(ex.ToString());
    54                return false;
    55            }
    56        }

    至此,整个测试运行过程结束。关于收集结果中除了主线程结果刷新之外,还需要有其他线程做结果的收集和展示,否则无法实现动态实时展示,这部分代码与QC无直接关系,也暂时不展示。

    可以看出,本文所涉及的内容,对于测试框架来说,也仅仅是一小部分。关于其他部分,以后有时间再分拆开逐一讨论。

  • 相关阅读:
    vue路由对象($route)参数简介
    原生js写ajax请求(复习)
    js,jq.事件代理(事件委托)复习。
    CSS 设置table下tbody滚动条
    H5 调用手机摄像机、相册功能
    css实现六边形图片(最简单易懂方法实现高逼格图片展示)
    input表单的type属性详解,不同type不同属性之间区别
    js对象,set和get方法 的三种实现形式
    day03 变量
    day01 js三种导入html的方法、js书写规范、变量的基本使用、变量提升
  • 原文地址:https://www.cnblogs.com/alphaxu/p/10188187.html
Copyright © 2011-2022 走看看