【工作经历:阿里巴巴搜索技术研发中心QA ,百度新产品测试部QA】 【领域:测试分析,自动化测试,性能测试,安全测试 】 【个人定位:高级测试工程师+培训师+领域产品专家】

发布新日志

  • Java单元测试的自动化与智能化

    2010-06-08 05:57:52

    被很多人写了N年了单元测试代码,早该淘汰了。
    Java的单元测试,其实是可以自动生成框架的。
    做Java的单元测试,不需要coding,只需要拖拽与填表i。

    Junit机制很强大,不过直接用来写单元测试,虽然小巧,简洁,高效。但是还是有更好的方式的。
    公司的JTester做的不错,我之前的想法,也都实现在里面了。是过不错的框架。

    不过,其实还可以再大胆的迈出几大步。

    提出如下几个设计想法
    1、实现对Java代码的scan,获得接口函数的类型。或者一些类与函数信息。
    2、对这些函数,根据反射分析出函数的类型,参数类型等。
    3、根据类型,生成对应的测试框架。包括测试数据,以及测试函数之间的调用关系。
    4、运行。然后分析结果


    最终的目标,就是作出一个工具,让一般人都可以通过自动的扫描,去勾选函数,拖拽函数之间的调用关系,去生成自动化的代码。以及逻辑测试体系。
    然后由用户去填写测试数据。去填充函数的调用。

    Java单元测试,其实也是可以自动化的。总体难度不大的,只不过一些处理依赖,代码细节等需要考虑。大部分的技术问题,都已经有成型的解决方案了。
    后续还可以通过mock方法,去自动学习函数库的测试数据。实现更好的自动化,或者智能化。
    这样测试人员的很多体力活,就可以消减了。留出精力去测试重点,分析难点。

    最近在学Java与JRuby。我会尽量去实现。也希望其他感兴趣的朋友,可以研究出更好的工具。





  • sikuli————GUI自动化新方法

    2010-02-06 21:21:59

    http://sikuli.org/

    美国理工学院学生的一个杰作,非常有意思。比较符合人类的思维。
    具体的例子可以去官方的文档。
    表面上看,是采用图像判断的方式去执行。
    支持测试使用。不过测试比较简单。

    使用了一下,发现不错。
    可以进行重新的封装与改造。用来做测试,是非常优秀的。

    它的工具的亮点,就是模拟了人是思维。
    目前GUI自动化的最大缺点,就是按照机器思维,而不是人的思维去实现。
    结果就导致了公司里面的自动化比较难以应对变化。
    UED的修改,对测试造成了很大的影响,给重用带来了不小的阻力。

    改进的方式,除了测试影响研发与UED外,还可以通过完善脚本来实现。
    其实,自动化,不要专注于某个自动化对象的name,或者id。
    而更应该关注的是对象的对人类可见的属性,比如带什么名字的按钮,什么颜色的按钮等。
    这样可以让传统的自动化脚本,可以更加的应对GUI变化。

    而sikuli,正是基于这个思路。

    不过sikuli的缺点也是很明显的。纯粹的图像比对是不能解决问题的,如果某个按钮的字体没有改变,但是颜色改变了,那么自动化也会是个问题。
    更好的GUI自动化,应该是针对人类视觉的模拟+传统方式的识别。

    sikuli的测试断言功能目前比较弱,只有
    assertExist与assertNotExist

    虽然目前它不够完善,管理机制不够健全,离项目应用还有很大的差距。
    但是值得一试。。。。

    小例子
    http://sikuli.org/documentation.shtml#/trac/wiki/HelloWorld





    下面是整理的几个相关链接

    5个基于图像识别的自动化工具

    sikuli只是新鲜并不代表流行


  • 后台测试自动化平台开发的第一个弯路

    2009-11-29 01:52:05

    平台开发以来,进度还是蛮快的。
    最近因为项目忙,疏于管理。结果项目走样了。犯了几个错误。

    1、Excel解析到TC,是一个兄弟开发的。他采用了GBK编码。因为Excel文件是GBK编码我们的系统默认的。
    而且Excel很难转换到UTF8,默认的excel没有提供此功能。
    我是一直推崇UTF8的。有点担心GBL,但是为了项目的统一性,我让大家修改了编码为GBK。结果麻烦就来了。
    很多ruby的库都是不支持GBK的,在运行,以及使用中,遇到了GBK的很多问题。耗费了不少时间。
    GBK始终是有隐患的。以后随着数据的处理复杂,以及与多系统,数据库,web进行协作的时候,这个编码也会成为隐患。

    2、模块是分开开发的。有两位DEV,在开发过程中,进行了大量的扩展。这也是我的失误,之前没有让他们了解透彻。中间又没有去check他们的进度与代码。导致了功能重复,设计思路有点乱。
    而且底层的两个哥们直接进行联调了,把框架架空了。
    进度可喜,对整体设计不利。把其他人架空了。

    3、接口之间的定义不是太清晰。导致一些关键的衔接,还需要重新去整理。


    接下来的解决方法。

    1、碍于excel的固有缺点,还是采用GBK去读,但是扩展一个方法,把GBK全部转换为utf8。系统还是采用utf8编码。
    2、我写一个主线联通的CASE,主动去联调所有的模块。把大家的注意力拉到框架上来。
    3、接口之间的定义,通过联调,最终确定下来。



    一些其他方面的计划。

    1、配置解析模块完全不要。为了更好的扩展,采用yaml或者xml去序列化,反序列化配置。Yaml不支持汉字,不果可以通过扩展实现支持汉字。测试已经通过了。具备可行性。
    2、编写几个常用的业务。去模拟使用整个框架。
    3、Project模块增加版本代码比对功能。
    4、项目二期启动。



    今天去听淘宝主办的《互联网测试技术交流会》,挺好的。只听了上午了演讲。很精彩。
    从分享中领悟到不少新的东西,对我们的平台设计很有启发。
    下午没有机会去听淘宝的测试平台框架介绍了。可惜。

    我们部门的框架还需要继续完善与调整,我相信,我们部门可以做到最优秀,可以超越淘宝的那个框架。
    我们可以更快的去实现他们没有实现的功能。

    好多的设想,我都压着,不想把这个平台前期做的很优秀。
    毕竟是集体开发的项目,先求稳,可以扩展。然后再去更好的设计。
    我对设计的要求还是很苛刻的,我是个完美主义者。我的创新思维也是很活跃的。
    所以我相信,我们可以做到最优秀。

    我们的技术小组,也可以做到同心协力,做出最优秀的产品。








  • 后台自动化测试框架设计初步设想

    2009-10-13 21:30:35


    最近正在为部门设计后台自动化测试框架。
    以前,其他部门的同事已经搞好了一个框架,写的蛮好的。
    当时因为自己业务基础太差,担心会沉溺于代码,所以就没有放功夫上去。
    现在终于可以有能力与信心去做了。


    之前的框架有一些小瑕龇
    1、配置解析比较臃肿
    2、脚本散乱,没有采用函数的形式,也没有一个完善的管理框架,管理麻烦,而且导致无法很好复用。
    3、模块化不够,使用一些自动化操作不方便。耦合严重,不利于通用。
    4、运行TC太慢。多设计一些并发可能效果会更好些。很多TC是可以并发的。



    本次的初步设计如下

    1、TC管理模块,尽量和之前的框架一致。减少重复劳动,也有利于以后的交流。
    2、对部门功能进行脚本的函数化,并纳入到函数库中进行统一管理。这部分脚本,同时可以为其他部门服务。
    3、无人值守。定期监控svn。一旦有代码变更,立即回归测试。
    4、把内存泄漏测试,代码覆盖率检测,c++代码静态测试,性能测试,以及代码性能监控测试全部纳入到平台中去。
    5、引入场景机制,可以实现多进程无不干扰的运行。
    6、模块化设计,让其他部门也可以借用其中的功能。
    7、配置部分重新设计,是个难点。如何更人性化的去配置,更容易让人使用是个难点。初步计划使用yaml来代替xml。这样会更简练。
    8、TC编写简化。真想考虑采用DSL去搞个自己的部门语言去实现。不过还是easy下,使用一般的方法去调用吧。
    9、数据构造分割为单独的部分,构造数据有必要深挖下。
    10、结果解析要更加简便。借鉴xwaitr结构。
    11、整体结构借鉴微软的那套自动化测试框架结构。


    前期整理需求,设计不幻想这么多。
    下两个周就要好好的画设计图了。


    几条纪律要牢记

    1、项目流程要规范
    2、进度安排要合理
    3、需求,设计文档要齐备
    4、不要另立山头,要多请教别人,尽量统一
    5、里程碑要把握好。

    最大的问题,是时间。大家都有项目,而且项目也很多,不能全身心的投入势必会影响进度的。
    只能挤时间了。
    最近沉默了很多,时间与精力不够,少管闲事,明哲保身暂时成为了座右铭。







  • 后台测试自动化稍早

    2009-08-20 21:59:50

    最近公司的同事开始了后台测试自动化的调研。
    能有这样的想法是很好的,之前搜索引擎端的自动化测试系统,就应用的非常好。
    但是要想建立一个崭新的框架来达到通用测试的地步,还是不太合适的。

    我积极的支持这个项目。不过自然也有不少的担心。在公司不方便说。在这里发泄下。
    我的观点是自动化测试稍早

    目前后台测试还不够深入。存在一系列的问题。


    1、测试功能点,测试范围不能捕获全面,导致测试用例设计不充分,覆盖率不够,很多bug都被pass过去了。
    公司举行过不少测试用例的设计,我也是经常去听。但是这些分析预计举例都是针对前台测试。虽然测试思路都是一样的。但是后台测试还是有一些概念是需要强化的。举个例子,如果一个产品的输入是一个网址,针对这个网址,前台的测试肯定是很多种的case,但是在后台,还要深入考虑更多的东西,比如这个网站是不是连接特别多,是不是单个网页的内容非常的大,是不是里面有一些特殊的字符,是否为flash,或者其他格式的内容等。这些都是后台需要关注的。

    2、业务,算法逻辑不精通。
    不少项目,都测试过去了,测试工程师还对他的算法与逻辑模棱两可,常常是一知半解。
    对我来说,这是无法容忍的。

    3、缺乏良好的后台测试思路
    定义测试范围,采用什么策略。
    可否利用数据驱动测试,可否采用自动化,是否进行充分的分析。
    有没有画过数据流,逻辑流,状态流。

    目前后台测试方面的问题多多,但是没有太多前人的经验与现成的资料可供借鉴。一起还需要积极的总结,总结项目经验,这些东西,都是要慢慢完善的。
    我已经开了一个课题,开始做总结,分析自己的,也借鉴别人的。

    这些问题,如果不能有效解决的话,后台测试的自动化就只能沦为一副空架子而已。

    不过小范围的自动化测试,倒是可以实现的。针对个别应用的架构是可以设计的。比如p4p引擎的测试。
    目前放慢了对技术的研究,开始总结一些非技术的因素。希望2个月的时间,可以出个文档出来。




  • 放弃qtp

    2008-08-25 16:00:06

    定位了新公司。qtp可能是用不到了。暂时不学习了。
    为了适应新公司,我需要学习linux,selenium,和jmeter。
    尽量多做一些准备,qtp的那本书,就送给朋友吧。
    一个人的技能,只有为公司所用,才具有真正的价值。
    有新机会了,就一定要努力。不枉费自己的决心。
  • qtp自动化测试实践即将看完

    2008-08-05 20:34:19

    当初说好是两个星期熟悉的。结果项目忙,又有些事情,导致了这个星期才开始看。
    也就是说,我只有4天的时间去看。我的看不过是走马观花,看的很快。
    直接看重点。关键部分做了笔注。

    结果两天就看完了大部分,还剩下几章,明天就可以看完了。
    书写的不错。qtp的要点都介绍到了。感觉就是把自己多年的经验堆积到一起。实用,全面,效率也高。
    非常适合像我这样的新手。

    不过底层的原理介绍有点少,可能因为作者面向的对象是广大的测试人员,精通windows底层的人不多。才这么安排的。前面的关于ui机制和反射的部分,倒是让我收获不小。
    书中大篇幅的介绍了qtp的描述性编程,一些控件的使用,而且个个都有代码例子,对每个控件的介绍也就是一两个方法或者属性,由此可以看出,作者经验丰富,编程老到,而且是标准的技术型人才。书中关于测试流程,测试策略,测试文档,测试管理的部分相对较少,也可以印证此点。


    总的来说,这本书非常的不错,非常实用,测试人员不可或缺的一本书。

  • 一些需要继续研究的小疑问

    2008-08-03 23:03:38

    几个小问题,下星期上班后继续反编译代码研究。

    1、如何使用maui进行web自动化。
    平时使用的东西,都是封装好了的。想看看最原始的代码是什么样的。熟悉一下流程。平时的例子也都是winform的。
    2、maui捕获ie的原理是什么。
    难道也是使用dom机制,从一个窗口中抓到dom。然后解析。
  • Reflector .net程序反编译工具的一些插件

    2008-08-01 19:06:25

    Reflector.FileDisassembler
    This add-in can be used to dump the disassembler output to files for any Reflector supported language.
    Website Download

     

    Reflector.CodeMetrics
    Analyses .NET assemblies and shows design quality metrics. The menu item is registered under the "Tools" menu.
    Website Download

     

    Reflector.SQL2005Browser
    This add-in allows to browse .NET assemblies stored in SQL Server 2005 (Yukon) databases.
    Website Download

     

    Reflector.DelphiLanguage
    The Delphi view that is used inside .NET Reflector provided as a language add-in.
    Website Download

     

    Reflector.McppLanguage
    This add-in extends Reflector with a Managed C++ language rendering module.
    Website Download

     

    Reflector.ChromeLanguage
    This add-in extends Reflector with a Chrome language rendering module.
    Website Download

     

    Reflector.Diff
    This add-in shows differences between two versions of the same assembly.
    Website Download

     

    Reflector.VisualStudio
    This program is hosting .NET Reflector inside the Visual Studio 2003 IDE. Run Reflector.VisualStudio.exe to register the add-in with Visual Studio.
    Website Download

     

    Reflector.ClassView
    Shows class definitions as plain text with color coding. The menu item is registered under the "Tools" menu.
    Website Download

     

    Reflector.CodeModelView
    This add-in shows the underlying code model objects for a selected node in .NET Reflector. The menu item is registered under the "Tools" menu.
    Website Download

     

    Reflector.FileGenerator
    This add-in can be used to dump the disassembler output to files for any Reflector supported language.
    Download

     

    Reflector.Graph
    This add-in draws assembly dependency graphs and IL graphs.
    Website Download

     

    Reflector.OpenRunningAssembly
    Opens an assembly or dependency from a process running on the system. The menu item is registered under the "Tools" menu.
    Website Download
  • web自动化初步探索

    2008-08-01 16:35:09

    这是关于mshtml的资料。msdn上的。以后会经常遇到。
    http://msdn.microsoft.com/en-us/library/aa752616(VS.85).aspx

    今天又重新反编译了maui的代码,看看它的html功能是如何实现的。
    基本功能是调用mshtml的接口。然后就去msdn上搜索资料去了。
    少数功能是调用一些非mshtml.dll文件以外的api。估计也是和mshtml的体系有关的。

    对于这些底层的东西,一定要弄透彻。
    对mshtml这个文件还不是太熟悉。需要熟悉一下。
  • 操纵遨游工具栏的方法,解决spy++无法捕获控件的问题(2)

    2008-07-31 17:48:06

    利用微软内部工具 mitaspy可以发现msaa对象。这说明,mita比maui要强。mita也是微软以后的标准。

    同时发现了FindWindowEx只能搜索子窗口,不能搜索孙子窗口,所以,需要提前注意窗口的嵌套层次。
    这个问题完结了。我也不研究了。继续其他的学习。

    更新后的代码


    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using Accessibility;

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                int hWnd = FindWindowEx(0, 0, "Maxthon2_Frame", null);
                int sub = FindWindowEx(hWnd, 0, "XTPDockBar", null);
                int subhwnd = FindWindowEx(sub, 0, null, "标准工具栏");
                if (subhwnd!= null)
                    Console.WriteLine("OK, Found the toolbar");
                msaa((IntPtr)subhwnd);

                Console.ReadKey();
            }

            [DllImport("oleacc", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
            public static extern int AccessibleChildren(IAccessible paccContainer, int iChildStart, int cChildren, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] object[] rgvarChildren, ref int pcObtained);

            [DllImport("oleacc.dll")]
            internal static extern int AccessibleObjectFromWindow(
                          IntPtr hwnd,
                          uint id,
                          ref Guid iid,
                          [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject);

            internal enum OBJID : uint
            {
                WINDOW = 0x00000000,
                SYSMENU = 0xFFFFFFFF,
                TITLEBAR = 0xFFFFFFFE,
                MENU = 0xFFFFFFFD,
                CLIENT = 0xFFFFFFFC,
                VSCROLL = 0xFFFFFFFB,
                HSCROLL = 0xFFFFFFFA,
                SIZEGRIP = 0xFFFFFFF9,
                CARET = 0xFFFFFFF8,
                CURSOR = 0xFFFFFFF7,
                ALERT = 0xFFFFFFF6,
                SOUND = 0xFFFFFFF5,
            }

            [DllImport("user32.dll")]
            public static extern int FindWindowEx(int hwndParent, int hwndChildAfter,
                string lpszClass, string lpszWindow);

            public static void msaa(IntPtr hwnd2)
            {
                object ōbj = new object();
                Guid guid = new Guid("{618736E0-3C3D-11CF-810C-00AA00389B71}");
                AccessibleObjectFromWindow(hwnd2, (uint)OBJID.WINDOW, ref guid, ref obj);
                IAccessible accessible = obj as IAccessible;
                accessible = accessible as IAccessible;
                Object[] childs = new Object[accessible.accChildCount];
                int ōbtained = 0;
                try
                {
                    AccessibleChildren(accessible, 0, accessible.accChildCount, childs, ref obtained);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                Console.WriteLine(accessible.accChildCount);
                Console.WriteLine(obtained.ToString());

                for (int i = 0; i < obtained; i++)
                {
                    IAccessible child = childs[i] as IAccessible;
                    Console.WriteLine(child.get_accName(0));                
                    Object[] accchilds = new Object[child.accChildCount];
                    int result = 0;
                    AccessibleChildren(child, 0, child.accChildCount , accchilds, ref result);
                    for (int j = 0; j < result; j++)
                    {
                        IAccessible jchild = accchilds[j] as IAccessible;
                        Console.WriteLine(jchild.get_accName(0));     
                    }
                }
            }
        }
    }





  • 操纵遨游工具栏的方法,解决spy++无法捕获控件的问题

    2008-07-31 17:03:41

    这个问题,相信很多从事自动化测试的人都会遇到。

    我初步想到的两个方法。经过实践已经可以了。
    这个问题算是可以放下了。
    以后开始主攻qtp。

    1、利用位置。工具栏的整个子窗口的绝对位置是可以得到的。里面的按钮大小都是相差无几的。所以,可以计算出里面的每个按钮的大体位置,发送一个点击消息,填充消息内容就可以了。


    2、利用msaa机制。
    利用标题,定位到遨游浏览器,以为工具栏的类名和其他控件的类名一样,所以,不能单独的通过FindWindowEx 来找到子窗口的句柄。(即使提供了标题参数,依然是定位不到,这个很奇怪,我没有深究,可能其他的api可以搞定)(搜索了一下资料,才发现原来中间还有窗口嵌套,FindWindowEx只能搜索子结点,不能搜索孙子节点)。我采用了EnumChildWindows函数。遍历里面的子窗体,根据属性判断出工具栏的窗体句柄。然后利用msaa的api得到msaa对象。枚举他的孩子对象,就可以得到所有的按钮的msaa对象了。

    然后利用msaa的机制,就可以操纵工具栏了。

    发送消息请参考
    http://www.cnblogs.com/karoc/archive/2006/11/29/576253.aspx



    以下是查找的关键代码。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using Accessibility;

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                int hWnd = FindWindowEx(0, 0, "Maxthon2_Frame", null);
                EnumChildWindows(hWnd, callBackEnumChildWindows, 0);
                Console.ReadKey();
            }

            [DllImport("oleacc", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
            public static extern int AccessibleChildren(IAccessible paccContainer, int iChildStart, int cChildren, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] object[] rgvarChildren, ref int pcObtained);

            [DllImport("oleacc.dll")]
            internal static extern int AccessibleObjectFromWindow(
                          IntPtr hwnd,
                          uint id,
                          ref Guid iid,
                          [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject);

            internal enum OBJID : uint
            {
                WINDOW = 0x00000000,
                SYSMENU = 0xFFFFFFFF,
                TITLEBAR = 0xFFFFFFFE,
                MENU = 0xFFFFFFFD,
                CLIENT = 0xFFFFFFFC,
                VSCROLL = 0xFFFFFFFB,
                HSCROLL = 0xFFFFFFFA,
                SIZEGRIP = 0xFFFFFFF9,
                CARET = 0xFFFFFFF8,
                CURSOR = 0xFFFFFFF7,
                ALERT = 0xFFFFFFF6,
                SOUND = 0xFFFFFFF5,
            }

            /// <summary>
            /// //////////////////////////////////
            [DllImport("user32.dll")]
            public static extern int FindWindowEx(int hwndParent, int hwndChildAfter,
                                                                               string lpszClass, string lpszWindow);
            [DllImport("user32.dll")]
            public static extern int GetWindowText(int hWnd, StringBuilder lpString, int nMaxCount);

            [DllImport("user32.dll")]
            public static extern int EnumChildWindows(int hWndParent, CallBack lpfn, int lParam);

            /// <summary>
            /// 回调函数代理
            /// </summary>
            public delegate bool CallBack(int hwnd, int lParam);

            /// <summary>
            /// 子窗口回调处理函数
            /// </summary>
            /// <param name="hwnd"></param>
            /// <param name="lParam"></param>
            /// <returns></returns>
            public static bool ChildWindowProcess(int hwnd, int lParam)
            {
                StringBuilder title = new StringBuilder(200);
                int len;
                len = GetWindowText(hwnd, title, 200);
                if (len < 1)
                    return true;
                if (title.ToString().Contains("标准工具栏"))
                {
                    Console.WriteLine("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
                    msaa((IntPtr)hwnd);
                    Console.WriteLine("ddddddddddddddddddddddddddddddddddddddddddddddddddddddddd");
                }
                Console.WriteLine(title + " " + hwnd);
                return true;
            }

            /// <summary>
            /// 子窗口回调函数代理
            /// </summary>
            public static CallBack callBackEnumChildWindows = new CallBack(ChildWindowProcess);

            public static void msaa(IntPtr hwnd2)
            {
                object ōbj = new object();
                Guid guid = new Guid("{618736E0-3C3D-11CF-810C-00AA00389B71}");
                AccessibleObjectFromWindow(hwnd2, (uint)OBJID.WINDOW, ref guid, ref obj);
                IAccessible accessible = obj as IAccessible;
                accessible = accessible as IAccessible;
                Object[] childs = new Object[accessible.accChildCount];
                int ōbtained = 0;
                try
                {
                    AccessibleChildren(accessible, 0, accessible.accChildCount, childs, ref obtained);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                Console.WriteLine(accessible.accChildCount);
                Console.WriteLine(obtained.ToString());

                for (int i = 0; i < obtained; i++)
                {
                    IAccessible child = childs[i] as IAccessible;
                    Console.WriteLine(child.get_accName(0));               
                    Object[] accchilds = new Object[child.accChildCount];
                    int result = 0;
                    AccessibleChildren(child, 0, child.accChildCount - 1, accchilds, ref result);
                    for (int j = 0; j < result; j++)
                    {
                        IAccessible jchild = accchilds[j] as IAccessible;
                        Console.WriteLine(jchild.get_accName(0));                    
                    }
                }
            }
        }
    }




  • MSAA 开发SDK

    2008-07-30 20:50:24

    http://www.microsoft.com/downloads/details.aspx?displaylang=en&familyid=4179742f-1f3d-4115-a8ba-2f7a6022b533

    这个SDK里所带的几个inspect object的工具还是不错的。


    下面是msaa的官方介绍
    http://msdn.microsoft.com/en-us/library/ms697707.aspx


    无意中,竟然发现国外一个比较不错的测试网站
    http://www.sqaforums.com/ubbthreads.php


    firefox官方竟然也有讨论
    http://www.mozilla.org/access/windows/msaa-server

    奇怪的是,我写的代码,竟然无法不能找到遨游的标准工具栏。真是奇怪。其他的就可以了。

           public static void msaa()
            {
                object ōbj = new object();
                IntPtr hWnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Maxthon2_Frame", null);
                IntPtr hwnd2 = FindWindowEx(hWnd, IntPtr.Zero, "XTPToolBar", "标准工具栏");
                Guid guid = new Guid("{618736E0-3C3D-11CF-810C-00AA00389B71}");

                AccessibleObjectFromWindow(hwnd2, (uint)OBJID.WINDOW, ref guid, ref obj);
                IAccessible accessible = obj as IAccessible;
                Object[] childs = new Object[accessible.accChildCount];
                int ōbtained = 0;
                AccessibleChildren(accessible, 0, accessible.accChildCount - 1, childs, out obtained);

                Console.WriteLine(obtained.ToString());


                for (int i = 0; i < obtained; i++)
                {
                    IAccessible child = childs[i] as IAccessible;
                    try { Console.WriteLine(child.get_accName(0)); }
                    catch { }
                    Object[] accchilds = new Object[child.accChildCount];
                    int result = 0;
                    AccessibleChildren(child, 0, child.accChildCount - 1, accchilds, out result);
                    for (int j = 0; j < result; j++)
                    {
                        IAccessible jchild = accchilds[j] as IAccessible;
                        try { Console.WriteLine(jchild.get_accName(0)); }
                        catch { }
                    }

                }
                Console.ReadLine();

            }

  • c#反射机制和一个简单的例子

    2008-07-30 19:29:15

    上次搜狐的面试人员问我自动化测试里反射的应用。我当时不知道反射机制的具体应用。
    回头看了一下,才终于明白了反射的定义。以前只知道是rename和得到当前的方法。
    没有想到,竟然可以执行自动化操作。怪不得他们问我了。

    研究了一下msdn。然后从里面提取了一个不错的小例子。对于理解反射非常有帮助。




    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                test1();
                Console.ReadKey();
            }
            public static void test1()
            {
                // Loads an assembly using its file name.
                Assembly a = Assembly.LoadFrom(@"F:\Documents and Settings\v-yahuan\Desktop\ttt\TestRun0501\WinApp\bin\Debug\WinApp.exe");
                // Gets the type names from the assembly.
                Type[] types2 = a.GetTypes();
                foreach (Type t in types2)
                {
                    Console.WriteLine(t.FullName);
                    foreach (MemberInfo m in t.GetMethods())
                    {
                        Console.WriteLine("methods " + m.Name);
                    }
                }
                Console.WriteLine();
            }


            public static void test2()
            {

                Type t = typeof(ConsoleApplication1.Program);
               
                Console.WriteLine("Listing all the members (public and non public) of the {0} type", t);

                // Lists static fields first.
                FieldInfo[] fi = t.GetFields(BindingFlags.Static |
                 BindingFlags.NonPublic | BindingFlags.Public);
                Console.WriteLine("// Static Fields");
                PrintMembers(fi);

                // Static properties.
                PropertyInfo[] pi = t.GetProperties(BindingFlags.Static |
                 BindingFlags.NonPublic | BindingFlags.Public);
                Console.WriteLine("// Static Properties");
                PrintMembers(pi);

                // Static events.
                EventInfo[] ei = t.GetEvents(BindingFlags.Static |
                 BindingFlags.NonPublic | BindingFlags.Public);
                Console.WriteLine("// Static Events");
                PrintMembers(ei);

                // Static methods.
                MethodInfo[] mi = t.GetMethods(BindingFlags.Static |
                 BindingFlags.NonPublic | BindingFlags.Public);
                Console.WriteLine("// Static Methods");
                PrintMembers(mi);

                // Constructors.
                ConstructorInfo[] ci = t.GetConstructors(BindingFlags.Instance |
                 BindingFlags.NonPublic | BindingFlags.Public);
                Console.WriteLine("// Constructors");
                PrintMembers(ci);

                // Instance fields.
                fi = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic |
                 BindingFlags.Public);
                Console.WriteLine("// Instance Fields");
                PrintMembers(fi);

                // Instance properites.
                pi = t.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic |
                 BindingFlags.Public);
                Console.WriteLine("// Instance Properties");
                PrintMembers(pi);

                // Instance events.
                ei = t.GetEvents(BindingFlags.Instance | BindingFlags.NonPublic |
                 BindingFlags.Public);
                Console.WriteLine("// Instance Events");
                PrintMembers(ei);

                // Instance methods.
                mi = t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic
                 | BindingFlags.Public);
                Console.WriteLine("// Instance Methods");
                PrintMembers(mi);

                Console.WriteLine("\r\nPress ENTER to exit.");
                Console.Read();
            }

            public static void PrintMembers(MemberInfo[] ms)
            {
                foreach (MemberInfo m in ms)
                {
                    Console.WriteLine("{0}{1}", "     ", m);
                }
                Console.WriteLine();

            }
        }
    }


  • 遍历一个窗口的子窗口代码

    2008-07-25 21:30:59


    从网上搜索到一段代码。我使用它来验证我的一个思想。
    spy++肯定是根据hwnd来遍历的。如果一个控件不能被spy++识别。就只能通过msaa来识别。
    这样是说hwnd和msaa并不是一一对应。疑问?

    这个代码是无法遍历到遨游的工具栏的。
    我使用msaa是可以的。

    同时也可以使用消息,通过spy++获取发送的消息,然后模拟就可以了。
    鼠标点击的位置是可以通过他们的父控件的位置来计算出来的。

    再想,也许可以有其他的方法。


           static void Main(string[] args)
            {
                //IsClientPopupWindows();
                EnumChildWindows(920262, callBackEnumChildWindows, 0);
                Console.ReadKey();
            }


            [DllImport("user32.dll")]
            public static extern int FindWindowEx(int hwndParent, int hwndChildAfter,
                                                                               string lpszClass, string lpszWindow);

            [DllImport("user32.dll")]
            public static extern int FindWindow(string strclassName, string strWindowName);


            [DllImport("user32.dll")]
            public static extern int GetLastActivePopup(int hWnd);

            [DllImport("user32.dll")]
            public static extern int AnyPopup();

            [DllImport("user32.dll")]
            public static extern int GetWindowText(int hWnd, StringBuilder lpString, int nMaxCount);

            [DllImport("user32.dll")]
            public static extern int EnumThreadWindows(int dwThreadId, CallBack lpfn, int lParam);

            [DllImport("user32.dll")]
            public static extern int EnumWindows(CallBack lpfn, int lParam);

            [DllImport("user32.dll")]
            public static extern int EnumChildWindows(int hWndParent, CallBack lpfn, int lParam);

            /// <summary>
            /// 回调函数代理
            /// </summary>
            public delegate bool CallBack(int hwnd, int lParam);


            /// <summary>
            /// 进程回调处理函数
            /// </summary>
            /// <param name="hwnd"></param>
            /// <param name="lParam"></param>
            /// <returns></returns>
            public static bool ThreadWindowProcess(int hwnd, int lParam)
            {
                EnumChildWindows(hwnd, callBackEnumChildWindows, 0);
                return true;
            }

            /// <summary>
            /// 窗口回调处理函数
            /// </summary>
            /// <param name="hwnd"></param>
            /// <param name="lParam"></param>
            /// <returns></returns>
            public static bool WindowProcess(int hwnd, int lParam)
            {
                EnumChildWindows(hwnd, callBackEnumChildWindows, 0);
                return true;
            }

            /// <summary>
            /// 子窗口回调处理函数
            /// </summary>
            /// <param name="hwnd"></param>
            /// <param name="lParam"></param>
            /// <returns></returns>
            public static bool ChildWindowProcess(int hwnd, int lParam)
            {
                StringBuilder title = new StringBuilder(200);
                int len;
                len = GetWindowText(hwnd, title, 200);
                if (len < 1)
                    return true;
                if(title.ToString().Contains("标准工具栏"))
                    EnumChildWindows(hwnd, callBackEnumChildWindows, 0);
                Console.WriteLine(title+" "+hwnd);
                return true;
            }


            /// <summary>
            /// 进程窗口回调函数代理
            /// </summary>
            public static CallBack callBackEnumThreadWindows = new CallBack(ThreadWindowProcess);

            /// <summary>
            /// 窗口回调函数代理
            /// </summary>
            public static CallBack callBackEnumWindows = new CallBack(WindowProcess);


            /// <summary>
            /// 子窗口回调函数代理
            /// </summary>
            public static CallBack callBackEnumChildWindows = new CallBack(ChildWindowProcess);

            /// <summary>
            /// 客户端是否弹出对话框
            /// </summary>
            /// <returns></returns>
            public static bool IsClientPopupWindows()
            {
                bool FindError = false;
                EnumWindows(callBackEnumWindows, 0);
                return FindError;
            }
  • 反编译maui

    2008-07-25 20:34:09

    maui是微软的一套测试框架。我们的项目使用的是基于它之上的一个框架。很多最终的操作还是要靠maui实现。今天突然想看看他的原理,就开始了我的反编译之旅。

    幸好maui是不加密的。解密没有任何困难。代码也很清晰。
    从它的对象初始化开始跟踪它的对象识别体系。
    从它的操作功能跟踪他的对象操纵机制。

    需要留意的是window的识别,甚至是msaa都是根据api来获得的。window和msaa是使用的不同的api。
    这表明这两个对象是基于不同的机制。

    最终终于跟到了最后,所有的功能都是根据系统api和windows的消息机制来模拟的。web控件的识别和操作是基于mshtml的。

    平时调试程序的时候多有不变。模拟鼠标和键盘的操作,自然也会对调试者形成干扰。大部分情况还是使用日志进行分析。
    感觉如果加入一个驱动处理层就可以了。

    比如调试者的计算机为客户端,被操纵的计算机为服务端。客户端将命令发送到服务端的特定的管道。
    比如文件,数据库,网络socket。服务端通过一个处于驱动处理层的程序,将所有的命令翻译为api和消息进行执行。这样就可以一台计算机上控制多台计算机进行case执行。

    总共加班了6个小时,终于有了初步的认识。
    对于以后解决问题,会有一些帮助。




  • Team Foundation与虚拟机

    2008-05-09 14:17:51

    公司里的vstf交给我管理了。
    公司的VSTF是建立在虚拟机上,这是为了方便数据的备份和转移。虚拟机不是采用的VM。而是微软的VPC。
    毕竟,在微软的网络里,使用微软的产品是比较恰当的。最起码,不需要花钱注册。
    我将一个虚拟机的文件copy到另一台计算机上,然后重新假设了一个虚拟机重新运行。本来打算想去认真的研究一下。但是发现会和原来的虚拟机冲突。
    这个冲突是无形的,因为一旦两个相同备份的虚拟机同时运行。后启动的虚拟机总会“抢掉”原来的虚拟机。
    两个虚拟机竟然会公用同一个ip。真是奇怪。修改了计算机名字也不行。
    两个装有vstf的虚拟机如果同时打开的话,就会报错。有意思。
    最近开始熟悉sharepoint,vstf。对这些新东西,真的是有点了解不多。





  • 工具改良计划

    2008-04-16 17:12:36

    工欲善其事,必先利其器。
    公司内部提供了一些代码生成工具,很好用,但是也有不少限制。打算改良一下。
    公司的那个工具,是根据窗口生成对应的控件的定义和初始化代码。
    以后的操作则是需要自己手工来写,这就是限制所在。
    一个窗体有很多的controls,光一个下拉菜单就可以让人找很大一会了。写起来也很费劲。
    所以,过程需要改良。

    我的设想是

    参考selenium的功能,通过一个数据文件来定义操作,然后通过编写的引擎来生成代码。
    小工具,却可以提高大效率。把这个作为自己的一个目标。
    两个月后完成。

    前期还是照样熟悉流程和规范,不能投机取巧。
  • 没有帮助的框架

    2008-04-10 15:13:16

    开始接触自动化测试框架。得到了源代码和一些例子。很多的封装函数没有给出说明。
    也没有文档,只能根据函数的名字进行猜测。或者去看源代码。
    大部分的信息都是口头流传的。
    虽然很混乱,但是还是很高兴的。
    乱世出英雄吗。呵呵。

    除了熟悉和实践这套测试框架之外,还要尝试着去diy一个自己的工具来提高效率。
    这就是工作的秘诀。
  • qtp的一个小小的毛病

    2008-04-09 11:30:31

    今天适用了一下qtp。因为在一片文章上看到有人说qtp不能支持gui功能测试。感觉很奇怪。就验证了一下。
    其实qtp是支持web和windows程序的。

    如果在record and setting中同时选择web and windows application .测试的时候,就会同时打开网页和windows程序。
    录制的时候,发现web和windows是同时录制的。以后要小心。不要被干扰了。

    以后一定要好好学习mi公司的qtp和loadrunner。这是最起码的技能。
211/212>
Open Toolbar