发布新日志

  • QTP中Action之间的调用以及参数传递

    2009-09-09 16:43:38Top 1

    QTP Action间的调用示例以及参数传递:

    本例使用简单的两个测试脚本说明参数的传递以及Action的调用,该例含2个Action, Action2调用Action1,在调用过程中传递参数给Action1做处理,同时获得相应的返回值.

    注意Action1是否使用ExitAction方法的区别.

    Action1的脚本:

    Option Explicit

    '添加两个参数:Input参数:inputPara, OutputPara参数:retOutputPara

    Dim inputPara
    inputPara = Parameter("strInputPara")

    Msgbox inputPara

    If Not inputPara = "" Then
     Parameter("retOutputPara") = "The output parameter is  " & inputPara
    Else
     Parameter("retOutputPara") = "The message is empty!"
    End If

    '使用ExitAction退出当前Action

    ExitAction "Exit the action"

     

    Action2的脚本:

    Option Explicit

    Dim returnMsg, returnMsg1, returnMsg2

    '第一种调用Action1的方法,同时msgbox出Output参数值.输入的参数为:"Hello!".

    RunAction "Action1", oneIteration, "Hello!", returnMsg
    Msgbox "returnMsg is: "  & returnMsg

    '第二种调用Action1的方法,同时利用QTP的Parameter对象获取调用Action1后的Output值.

    RunAction "Action1", oneIteration, "Hello!"
    returnMsg1 = Parameter("Action1", "retMessage")
    Msgbox "returnMsg1 is: " & returnMsg1

    '第三种调用Action1的方法,请注意此时返回的值.

    returnMsg2 = RunAction("Action1", oneIteration, "Hello!")
    Msgbox "returnMsg2 is : " & returnMsg2

    '注:使用第三种方法调用Action1的返回值为Action1中使用ExitAction时的参数值:"Exit the action"

    有兴趣的朋友,可以使用该脚本到QTP的实际环境中测试一下.

  • jxl对M$ Excel 进行全操作

    2014-04-08 16:32:22

    分类: Java

        作为一个完整的 MIS 系统,报表功能是必不可少的,但是尽管报表为用户提供了强大的可操作性功能,但是,对于用户的某些统计需求来说,如果采用开发报表的方式来解决就有点像 “拿着牛刀去杀鸡!”人为地提升了系统的开发成本和维护成本。因此,应用系统对 Excel 的读、写、更新操作就变成非常有效的(低成本)解决方案。
    此外,由于 Excel 的出众表现,致使其在很多应用系统的日志管理子系统中如日中天。
    自从 M$ 公开了 OFFICE 的编码格式以来,很多开源的组织都提供了对 Excel 支持读写操作的插件包。Java 世界里,Appache应该算是开源世界中的领头羊。他的Jakarta Project 中的 POI Project 就提供了对OFFICE的完美支持(不过最近好像他对Word的支持项目已经停止,而且公开在网站上圈人呢 ^^ ,如果你对Word文件的编码格式非常熟悉,可以发封邮件哦!),当然除此之外,还有很多其他开源组织也对 Excel 的读写操作提供了很好的支持,其中简单而且实用的便是 Display-tag ,程序员根本就不需要考虑如何将数据合理地写入Excel中,这一切都是由其 Servlet 自动完成,但也正是由于他对程序员是透明的,因此为很多操作也带来了不便。在接下来的文字中我会介绍另外一种同样对Excel提供了完美支持的第三方插件 JXL ,Java Excel是一开放源码项目,通过它Java开发人员可以读取Excel文件的内容、创建新的Excel文件、更新已经存在的Excel文件。使用该 API非Windows操作系统也可以通过纯Java应用来处理Excel数据表。因为是使用Java编写的,所以我们在Web应用中可以通过JSP、 Servlet来调用API实现对Excel数据表的访问。
    Jakarta 的 POI Project 与 Java Excel API 在开源世界中可以说是并驾齐驱,但是也各有优劣,poi在某些细节有些小Bug并且不支持写入图片,其他方面都挺不错的;而JXL提供了对图片的支持,问 题就是对公式支持不是很好,但还是提供了简单的公式读取支持。因此你的项目中要选用什么样的第三方插件为完全由你的应用来决定。如果你的软件是跟财务有相 当的关系的话,建议采用 POI Project,就我所在目前的项目来说由于用不到计算公式,而且很可能需要导出图片,因此,我的选择是 JXL 。其提供的支持如下:
     
            从Excel 95、97、2000等格式的文件中读取数据
            读取Excel公式(可以读取Excel 97以后的公式)
            生成Excel数据表(格式为Excel 97)
            支持字体、数字、日期的格式化
            支持单元格的阴影操作,以及颜色操作
            修改已经存在的数据表
     
    下面就我对JXL的使用情况以简单的小程序为例子作一些总结,希望对大家能够有所帮助:
     
    1.读取Excel中的数据信息:

     
    public void getExcel() throws Exception{
            try{
                    InputStream excelResource=new FileInputStream("D:\\myfile.xls");
                    Workbook rwb=Workbook.getWorkbook(excelResource);
                    int sheetCount=rwb.getNumberOfSheets();
                    Sheet rs=rwb.getSheet(0);
                    int rows=rs.getRows();
                    int cols=rs.getColumns();
                    for(int i=0;i<rows;i++){
                            String theCell_00=rs.getCell(0,i).getContents();
                            System.out.println("the Cell Content : "+theCell_00);
                    }
                    rwb.close();
            } catch(Exception ex){
                    ex.printStackTrace();
                    ex.getMessage();
            }
    }
     

    说明:Excel读取的流程为:
                    《1》打开工作簿
                    《2》打开工作Sheet区
                    《3》找到某行某列的数据值
          需要强调的是,在获取单元格时,按照我们的通常处理一般都是“先行后列”getCell(row,col),但是,在JXL中却恰恰相反,是“先列后行”getCell(col,row)!最后一定要记着把打开的Excel要关闭:rwb.close();
     
    2.向Excel中写入数据信息:

     
    public void setExcel(String values) throws IOException,Exception{
            try{
                    WorkbookSettings seting=new WorkbookSettings();
                    File file=new File("D:\\myfile.xls");
                    WritableWorkbook wwb=Workbook.createWorkbook(file);
                    WritableSheet wst=wwb.createSheet("mySheet",0);
                    //Label myLabel=new Label(0,0,"asfasdfasdfasdf");
                    for(int i=0;i<10;i++){
                            Label myLabel1=new Label(0,i,"汉字");
                            wst.addCell(myLabel1);
                    }
                    wst.addCell(myLabel);
                    String str=wst.getCell(0,0).getContents();
                    System.out.println(str);
                    wwb.write();
                    wwb.close();
            } catch(Exception e){
                    e.printStackTrace();
                    e.getMessage();
                    throw new Exception(e);
            }
    }
     

    说明:此方法中用到了jxl下write包中的一些类,因此,要使上面这段程序能够调试通过,引入相关的类是相当必要的。在程序最后写完所有数 据后一定要记着 wwb.write(); 最后wwb.close();如果不调用方法write,则数据不会被写入到Excel中,因此....切记。
     
    3.更新Excel中的数据:

     
     

     
     
    详细说明:
    Java Excel API提供了许多访问Excel数据表的方法,在这里我只简要地介绍几个常用的方法,其它的方法请参考附件中的Java Excel API Document。

    Workbook类提供的方法

    1. int getNumberOfSheets()获得工作薄(Workbook)中工作表(Sheet)的个数,示例:

    jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
    int sheets = rwb.getNumberOfSheets();

    2. Sheet[] getSheets()返回工作薄(Workbook)中工作表(Sheet)对象数组,示例:

    jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
    Sheet[] sheets = rwb.getSheets();

    3. String getVersion()返回正在使用的API的版本号,好像是没什么太大的作用。

    jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
    String apiVersion = rwb.getVersion();

    Sheet接口提供的方法

    1) String getName()获取Sheet的名称,示例:

    jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
    jxl.Sheet rs = rwb.getSheet(0);
    String sheetName = rs.getName();

    2) int getColumns()获取Sheet表中所包含的总列数,示例:

    jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
    jxl.Sheet rs = rwb.getSheet(0);
    int rsColumns = rs.getColumns();

    3) Cell[] getColumn(int column)获取某一列的所有单元格,返回的是单元格对象数组,示例:

    jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
    jxl.Sheet rs = rwb.getSheet(0);
    Cell[] cell = rs.getColumn(0);

    4) int getRows()获取Sheet表中所包含的总行数,示例:

    jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
    jxl.Sheet rs = rwb.getSheet(0);
    int rsRows = rs.getRows();

    5) Cell[] getRow(int row)获取某一行的所有单元格,返回的是单元格对象数组,示例子:

    jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
    jxl.Sheet rs = rwb.getSheet(0);
    Cell[] cell = rs.getRow(0);

    6) Cell getCell(int column, int row)获取指定单元格的对象引用,需要注意的是它的两个参数,第一个是列数,第二个是行数,这与通常的行、列组合有些不同。

    jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
    jxl.Sheet rs = rwb.getSheet(0);
    Cell cell = rs.getCell(0, 0);
     
    下面的代码主要是向大家介绍如何生成简单的Excel工作表,在这里单元格的内容是不带任何修饰的(如:字体,颜色等等),所有的内容都作为字符串写入。(完整代码见ExcelWriting.java)

    与 读取Excel工作表相似,首先要使用Workbook类的工厂方法创建一个可写入的工作薄(Workbook)对象,这里要注意的是,只能通过API提 供的工厂方法来创建Workbook,而不能使用WritableWorkbook的构造函数,因为类WritableWorkbook的构造函数为 protected类型。示例代码片段如下:



    import java.io.*;
    import jxl.*;
    import jxl.write.*;
    … … … …
    try{
            //构建Workbook对象, 只读Workbook对象
            //Method 1:创建可写入的Excel工作薄
            jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile));
            //Method 2:将WritableWorkbook直接写入到输出流
           /*
                OutputStream s = new FileOutputStream(targetfile);
                jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(os);
            */
    }catch (Exception e){
            e.printStackTrace();
    }
     


    API提供了两种方式来处理可写入的输出流,一种是直接生成本地文件,如果文件名不带全路径的话,缺省的文件会定位在当前目录,如果文件名带有全 路径的话,则生成的Excel文件则会定位在相应的目录;另外一种是将Excel对象直接写入到输出流,例如:用户通过浏览器来访问Web服务器,如果 HTTP头设置正确的话,浏览器自动调用客户端的Excel应用程序,来显示动态生成的Excel电子表格。

    接下来就是要创建工作表,创建工作表的方法与创建工作薄的方法几乎一样,同样是通过工厂模式方法获得相应的对象,该方法需要两个参数,一个是工作表的名称,另一个是工作表在工作薄中的位置,参考下面的代码片段:

    //创建Excel工作表
    jxl.write.WritableSheet ws = wwb.createSheet("Test Sheet 1", 0);
    常见的处理如下所示:
     

    //1.添加Label对象
    jxl.write.Label labelC = new jxl.write.Label(0, 0, "This is a Label cell");
    ws.addCell(labelC);

    //添加带有字型Formatting的对象
    jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true);
    jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf);
    jxl.write.Label labelCF = new jxl.write.Label(1, 0, "This is a Label Cell", wcfF);
    ws.addCell(labelCF);

    //添加带有字体颜色Formatting的对象
    jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false,
    UnderlineStyle.NO_UNDERLINE, jxl.format.Colour.RED);
    jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc);
    jxl.write.Label labelCFC = new jxl.write.Label(1, 0, "This is a Label Cell", wcfFC);
    ws.addCell(labelCF);

    //2.添加Number对象
    jxl.write.Number labelN = new jxl.write.Number(0, 1, 3.1415926);
    ws.addCell(labelN);

    //添加带有formatting的Number对象
    jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##");
    jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf);
    jxl.write.Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN);
    ws.addCell(labelNF);

    //3.添加Boolean对象
    jxl.write.Boolean labelB = new jxl.write.Boolean(0, 2, false);
    ws.addCell(labelB);

    //4.添加DateTime对象
    jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, new java.util.Date());
    ws.addCell(labelDT);

    //添加带有formatting的DateFormat对象
    jxl.write.DateFormat df = new jxl.write.DateFormat("dd MM yyyy hh:mm:ss");
    jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df);
    jxl.write.DateTime labelDTF = new jxl.write.DateTime(1, 3, new java.util.Date(), wcfDF);
    ws.addCell(labelDTF);

     
    注意:
    1.在构造单元格时,单元格在工作表中的位置就已经确定了。一旦创建后,单元格的位置是不能够变更的,尽管单元格的内容是可以改变的。
    2.单元格的定位是按照下面这样的规律(column, row),而且下标都是从0开始,例如,A1被存储在(0, 0),B1被存储在(1, 0)。
     
    最后,不要忘记关闭打开的Excel工作薄对象,以释放占用的内存,参见下面的代码片段:


    //写入Exel工作表
    wwb.write();

    //关闭Excel工作薄对象
    wwb.close();

    拷贝、更新Excel工作薄

    接下来简要介绍一下如何更新一个已经存在的工作薄,主要是下面二步操作,第一步是构造只读的Excel工作薄,第二步是利用已经创建的Excel工作薄创建新的可写入的Excel工作薄,参考下面的代码片段:

    //创建只读的Excel工作薄的对象
    jxl.Workbook rw = jxl.Workbook.getWorkbook(new File(sourcefile));

    //创建可写入的Excel工作薄对象
    jxl.write.WritableWorkbook  wwb = Workbook.createWorkbook(new File(targetfile), rw);
                
    //读取第一张工作表
    jxl.write.WritableSheet ws = wwb.getSheet(0);

    //获得第一个单元格对象
    jxl.write.WritableCell wc = ws.getWritableCell(0, 0);
                
    //判断单元格的类型, 做出相应的转化
    if(wc.getType() == CellType.LABEL)
    {
    Label l = (Label)wc;
        l.setString("The value has been modified.");
    }

    //写入Excel对象
    wwb.write();

    //关闭可写入的Excel对象
    wwb.close();

    //关闭只读的Excel对象
    rw.close();



    之所以使用这种方式构建Excel对象,完全是因为效率的原因,因为上面的示例才是API的主要应用。为了提高性能,在读取工作表时,与 数据相关的一些输出信息,所有的格式信息,如:字体、颜色等等,是不被处理的,因为我们的目的是获得行数据的值,既使没有了修饰,也不会对行数据的值产生 什么影响。唯一的不利之处就是,在内存中会同时保存两个同样的工作表,这样当工作表体积比较大时,会占用相当大的内存,但现在好像内存的大小并不是什么关 键因素了。

    一旦获得了可写入的工作表对象,我们就可以对单元格对象进行更新的操作了,在这里我们不必调用API提供的add()方法,因为单元格已经于工作表当中,所以我们只需要调用相应的setXXX()方法,就可以完成更新的操作了。

    尽单元格原有的格式化修饰是不能去掉的,我们还是可以将新的单元格修饰加上去,以使单元格的内容以不同的形式表现。

    新生成的工作表对象是可写入的,我们除了更新原有的单元格外,还可以添加新的单元格到工作表中,这与示例2的操作是完全一样的。

    最后,不要忘记调用write()方法,将更新的内容写入到文件中,然后关闭工作薄对象,这里有两个工作薄对象要关闭,一个是只读的,另外一个是可写入的。
     
  • Sql Server Instance 相关

    2013-07-22 13:43:46

    所谓“SQL实例”,实际上就是SQL服务器引擎,每个SQL Server数据库引擎实例各有一套不为其他实例共享的系统及用户数据库。
    在一台计算机上,可以安装多个SQL SERVER,每个SQL SERVER就可以理解为是一个实例。
    实例又分为“默认实例”和“命名实例”,如果在一台计算机上安装第一个SQLSERVER,命名设置保持默认的话,那这个实例就是默认实例。
    一台计算机上最多只有一个默认实例,也可以没有默认实例,默认实例名与计算机名相同。
    所以说,默认实例的名称是与计算机名相同,而不是称为"local",但一般情况下,如果要访问本机上的默认SQL服务器实例,使用计算机名、(local)、localhost、127.0.0.1、. 、本机IP地址,都可以达到相同的目的。
    但如果要访问非本机的SQL服务器,那就必须使用计算机\实例名的办法。
     
    如果要修改自定义的实例变为默认实例,需要把TCP端口变为默认端口1433,以及IP1由Mac地址变成IP地址,详细为:Configuration Manager > SQL Server Network Configuration > Protocols for <InstanceName> > TCP/IP 属性 > IP1 Address(默认为Mac地址,修改为主机IP); IP All (TCPDynamicPorts:1433). 由此,用SQL client 去连接,直接连(local)就行了。
     
  • 是工作养你还是你养工作?

    2013-02-26 10:06:14

     转摘:
    经过这几年的受薪生活,我发现,其实工作可 以分为2种,一种是你养着的,一种是养着你的。为什么说是你养着工作呢?因为这份工作除了金钱和其他物质条件,不提供其他东西给你或者提供的很少。用它给 的那份钱,虽然你活命了,但却要用自己的知识、兴趣和业余时间获得的满足感,进行自我激励,并在工作中奉献。虽然奉献了,却看不到这个奉献带回来满足感或 者只有非常微小的经验、创造、满足感。即使有升职加薪作为你工作成绩的肯定,但兴奋很快就过去,你始终要面对的还是同样一个局面:被消耗的感觉。

      另一种工作可能给你很少的报酬,很艰苦的工作环境, 但它实现了你自己的才能,让你在经验中更进一步的认识自己,肯定自己。你有机会得到大大小小的成就,这些更新的自我认识变成了你进步的最大资源,于是可以 说,从这个角度上它才是养活了你。回顾我所经历的工作,那份工作环境最差,老板人品平平,艰苦挣扎的工作,竟然给了我最大的报酬
  • Deadlock 总结

    2012-08-27 14:34:40

    死锁是由于并发进程只能按互斥方式访问临界资源等多种因素引起的,并且是一种与执行时间和速度密切相关的错误现象。

         死锁的定义:若在一个进程集合中,每一个进程都在等待一个永远不会发生的事件而形成一个永久的阻塞状态,这种阻塞状态就是死锁。

          死锁产生的必要条件:

          1.互斥mutual exclusion):系统存在着临界资源;

          2.占有并等待(hold and wait):已经得到某些资源的进程还可以申请其他新资源;

          3.不可剥夺(no preemption):已经分配的资源在其宿主没有释放之前不允许被剥夺;

          4.循环等待(circular waiting):系统中存在多个(大于2个)进程形成的封闭的进程链,链中的每个进程都在等待它的下一个进程所占有的资源;

    死锁的示例和解决方法

          首先我们在数据库tempdb中创建两个表DlTable1和DlTable2,它们都包含两个字段分别是Id和Name,接着我们往这两个表中插入数据,具体SQL代码如下:

    -- Note we use tempdb for testing.
    USE tempdb

    -- Create datatable in tempdb.
    CREATE TABLE DlTable1 (DL1Id INT, DL1Name VARCHAR(20))
    CREATE TABLE DlTable2 (DL2Id INT, DL2Name VARCHAR(20))


    -- Insert multiple data into DlTable1 and DlTable2 in SQL Server 2005.
    INSERT INTO DlTable1
    SELECT 1, 'Deadlock'
    UNION ALL
    SELECT
    2, 'JKhuang'
    UNION ALL
    SELECT
    3, 'Test'
    GO

    INSERT INTO DlTable2
    SELECT 1, 'Deadlock'
    UNION ALL
    SELECT
    2, 'JacksonHuang'
    UNION ALL
    SELECT
    3, 'Test'
    GO

      接着我们打开两个查询窗口分别创建两个独立的事务A和B如下:

    -- In query window 1. USE tempdb GO -- Create transaction A. BEGIN TRANSACTION UPDATE DlTable1 SET DL1Name = 'Uplock' WHERE DL1Id = 2 -- Delay 23 second. WAITFOR DELAY '00:00:23' UPDATE DlTable2 SET DL2Name = 'Downlock' WHERE DL2Id = 2 ROLLBACK TRANSACTION -- In query window 2. USE tempdb GO -- Create transaction B. BEGIN TRANSACTION UPDATE DlTable2 SET DL2Name = 'Downlock' WHERE DL2Id = 2 -- Delay 23 second. WAITFOR DELAY '00:00:23' UPDATE DlTable1 SET DL1Name = 'Uplock' WHERE DL1Id = 2 ROLLBACK TRANSACTION

         上面我们定义了两个独立的事务A和B,为了测试死锁这里我们使用WAITFOR DELAY使事务执行产生延时。




    -- Insert multiple data into DlTable1 and DlTable2 in SQL Server 2008.
    INSERT INTO DlTable1 VALUES (1, 'Deadlock'), (2, 'JKhuang'), (3, 'Test')
    INSERT INTO DlTable2 VALUES (1, 'Deadlock'), (2, 'JacksonHuang'), (3, 'Test')

        现在我们执行以上SQL代码成功创建了DlTable1和DlTable2并且插入了数据。

    现在我们使用SQL Server Profiler分析死锁

          在本节中,我们将看到如何使用SQL Server Profiler来捕获死锁跟踪。

          1.启动SQL Server事件探查器和连接所需的SQL Server实例

          2.创建一个新的跟踪

          3.在事件选择页中,取消默认事件选项,我们选择“死锁图形”事件、 “锁定:死锁”和“锁定:死锁链”如下图所示:

    deadlock8

    图8事件选择设置

         4. 启动一个新的跟踪

         5.在SSMS中,开两个查询窗口#1和#2,我们重新执行前面两个事务

         6.事务执行结束,一个执行成为,另一个发生死锁错误

         7.我们打开事件探查器,如下图所示:

    deadlock11

    图9 Deadlock graph

         8.选择Deadlock graph,我们可以直观查看到两个事务之间发生死锁的原因

    deadlock17

    图10 事务进程A

          上图的椭圆形有一个叉,表示事务A被SQL Server选择为死锁牺牲品,如果我们把鼠标指针移动到椭圆中会出现一个提示。

    deadlock16

    图11 事务进程B

          上图的椭圆形表示进程执行成功,我们把鼠标指针移动到椭圆中也会出现一个提示。

          中间的两个矩形框称为资源节点,它们代表的数据库对象,如表,行或索引。由于事务A和B在拥有各自资源时试图获得对方资源的一个独占锁,使得进程相互等待对方释放资源从而导致死锁。

    死锁避免:

         现在让我们回顾一下上了死锁的四个必要条件:互斥,占有并等待,不可剥夺和循环等待;我们只需破坏其中的一个或多个条件就可以避免死锁发生,方法如下:

         (1).按同一顺序访问对象。(注:避免出现循环,降低了进程的并发执行能力)

         (2).避免事务中的用户交互。(注:减少持有资源的时间,减少竞争)

         (3).保持事务简短并处于一个批处理中。(注:同(2),减少持有资源的时间)

         (4).使用较低的隔离级别。(注:使用较低的隔离级别(例如已提交读)比使用较高的隔离级别(例如可序列化)持有共享锁的时间更短,减少竞争)

         (5).使用基于行版本控制的隔离级别:2005中支持快照事务隔离和指定READ_COMMITTED隔离级别的事务使用行版本控制,可以将读与写操作之间发生的死锁几率降至最低:

         SET ALLOW_SNAPSHOT_ISOLATION ON --事务可以指定 SNAPSHOT 事务隔离级别;

         SET READ_COMMITTED_SNAPSHOT ON --指定 READ_COMMITTED 隔离级别的事务将使用行版本控制而不是锁定。默认情况下(没有开启此选项,没有加with nolock提示),SELECT语句会对请求的资源加S锁(共享锁);而开启了此选项后,SELECT不会对请求的资源加S锁。

          注意:设置 READ_COMMITTED_SNAPSHOT选项时,数据库中只允许存在执行 ALTER DATABASE命令的连接。在 ALTER DATABASE完成之前,数据库中决不能有其他打开的连接。数据库不必一定要处于单用户模式中。

         在数据库中设置READ COMMITTED SNAPSHOT 或 ALLOW SNAPSHOT ISOLATIONON ON时,查询数据时不再使用请求共享锁,如果请求的行正被锁定(例如正在被更新),SQL_Server会从行版本存储区返回最早的关于该行的记录(SQL_server会在更新时将之前的行数据在tempdb库中形成一个链接列表。(详细请点这里这里

    ALTER Database DATABASENAME SET READ_COMMITTED_SNAPSHOT ON

         (6).使用绑定连接。(注:绑定会话有利于在同一台服务器上的多个会话之间协调操作。绑定会话允许一个或多个会话共享相同的事务和锁(但每个回话保留其自己的事务隔离级别),并可以使用同一数据,而不会有锁冲突。可以从同一个应用程序内的多个会话中创建绑定会话,也可以从包含不同会话的多个应用程序中创建绑定会话。在一个会话中开启事务(begin tran)后,调用exec sp_getbindtoken @Token out;来取得Token,然后传入另一个会话并执行EXEC sp_bindsession @Token来进行绑定(最后的示例中演示了绑定连接)。

    解决死锁

          这里有几个方法可以帮助我们解决死锁问题。

          优化查询

          我们在写查询语句时,要考虑一下查询是否Join了没有必要的表?是否返回数据太多(太多的列或行)?查询是否执行表扫描?是否能通过调整查询次序来避免死锁?是否应该使用Join的地方使用了Left Join?Not In语句是否考虑周到?

          我们在写查询语句可以根据以上准则来考虑查询是否应该做出优化。

          慎用With(NoLock)

          默认情况下SELECT语句会对查询到的资源加S锁(共享锁),由于S锁与X锁(排他锁)不兼容,在加上With(NoLock)后,SELECT不对查询到的资源加锁(或者加Sch-S锁,Sch-S锁可以与任何锁兼容);从而使得查询语句可以更好和其他语句并发执行,适用于表数据更新不频繁的情况。

         也许有些人会提出质疑With(NoLock),可能会导致脏读,首先我们要考虑查询的表是否频繁进行更新操作,而且是否要读回来的数据会被修改,所以衡量是否使用With(NoLock)还是要根据具体实际出发。

         优化索引

         是否有任何缺失或多余的索引?是否有任何重复的索引?

         处理死锁

         我们不能时刻都观察死锁的发生,但我们可以通过日志来记录系统发生的死锁,我们可以把系统的死锁错误写入到表中,从而方便分析死锁原因。

         缓存

         也许我们正在执行许多相同的查询非常频繁,如果我们把这些频繁的操作都放到Cache中,执行查询的次数将减少发生死锁的机会。我们可以在数据库的临时表或表,或内存,或磁盘上应用Cache。

  • Oracle space quota exceeded -- ORA-01536

    2012-07-09 11:41:46

    In one of my support case,I get error like " java.sql.SQLException: ORA-01536: space quota exceeded for tablespace 'PXTRG_D' "

    The cause of ORA-01536 is that the space quota for the segment owner in the tablespace has been exhausted and the operation attempted the creation of a new segment extent in the tablespace.

    The action is that either drop unnecessary objects in the tablespace to reclaim space or have a privileged user increase the quota on this tablespace for the segment owner.

    You can increase the quota on the tablespace as follows:


    alter   user   <username>  quota   unlimited   on   PXTRG_D;
    grant   unlimited   tablespace   to   <username>

    ============
    select * from dba_role_privs where GRANTEE= 'username' ; //check the user's role
    select * from dba_sys_privs where GRANTEE= 'username' ; // check the  privilege
    SELECT * FROM DBA_TS_QUOTAS; // list all tablespace quotas
    ================
  • Oracle 导入异常 Data Pump Issue-- ORA-02304

    2012-07-09 11:27:21

    今天有同事导入数据库时,当create type时候报错,我在原有数据库A(同一数据库下建立多个用户)测试很多遍都出现这个问题。新创建数据B导入没有问题。
    =======
    Import: Release 11.1.0.7.0 - Production on Monday, 18 June, 2012 13:40:16


    Copyright (c) 2003, 2007, Oracle.  All rights reserved.


    Connected to:
     Oracle Database 11g Release 11.1.0.7.0 - 64bit Production
    "SYSTEM". "TEST_IMP_01_META" master table has been loaded / unloaded normal
    start SYSTEM."TEST_IMP_01_META": system/********@px-h parfile=imptest_01_meta.par
    Object type SCHEMA_EXPORT/SYSTEM_GRANT is in the process
    Object type SCHEMA_EXPORT/DEFAULT_ROLE is in the process
    Object type SCHEMA_EXPORT/PRE_SCHEMA/PROCACT_SCHEMA is in the process
    Object type SCHEMA_EXPORT/SYNONYM/SYNONYM is in the process
    Object type SCHEMA_EXPORT/TYPE/TYPE_SPEC is in the process
    ORA-39083: Object type TYPE create failed with the following error: 
    ORA-02304: Literal object identifier is invalid.
    Error statement is as follows:
    CREATE TYPE "PXTRG_VUT2"."TEST_OBJ01"   OID '6ECBDDE4D6E2DBBAE040D69ECBBD670E' as object
    (VAR_PART_NUMBER VARCHAR2(100),
    VAR_LOT_NAME VARCHAR2(100));
    ORA-39083: Object type TYPE create failed with the following error: 
    ORA-02304: Literal object identifier is invalid.
    Error statement is as follows:
    CREATE TYPE "PXTRG_VUT2"."SYS_PLSQL_298873_9_2"   OID '6F4529EF4C50228FE040D69ECABD15EA' as object (VAR_PART_NUMBER VARCHAR2(100),
    VAR_LOT_NAME VARCHAR2(100),
    VAR_ACCOUNT VARCHAR2(100),
    VAR_VPART_NUMBER VARCHAR2(100),
    VAR_COMP_REASON VARCHAR2(100));
    ===============================

    查看ora-02304错误原因如下:

    ORA-02304invalid object identifier literal

    在尝试用impdp把一个dump文件从一个schema导入到另外一个schema (两个schema位于同一个数据库上,需要用到remap_schema参数来进行schema的映射转换)遇到了诸多类似如下的错误.... (cmd> impdp test/test directory=dump_dir dumpfile=dump.dmp logfile=dump.log remap_schema=frank:test)
    ORA-39083Object type TYPE failed to create with error:
    ORA-02304invalid object identifier literal

    注意到在创建type的时候,“多出来”一段OID的东东,很是奇怪,因为平时在创建一个type的时候,根本就不会涉及到指定OID的问题, 问题八成就是出在这个OID上。那么这个OID到底是什么呢?为什么会导致ORA-02304的错误呢?

     

    OID 应该就是Object Identifier, 在数据库中每个object都有自己唯一的标识,也就是object id. 因此把一个Object从一个schema导入到另外一个schema的时候(在同一个数据库上),如果这个OID也保持不变的话,那么就会出现多个 Object共享同一个object id的问题,这显然是不行的。因此会出现这个invalid object identifier的问题。

     

    那么如何解决这个问题呢? 通过impdp help=y 可以看到有一个参数来解决这个问题,

    TRANSFORM             Metadata transform to apply to applicable objects.
                          Valid transform keywords: SEGMENT_ATTRIBUTES, STORAGE
                          
    OIDand PCTSPACE.


    看看帮助文档,得到如下信息,

     

    OID - If the value is specified as n, the assignment of the exported OID during the 

    creation of object tables and types is inhibited. Instead, a new OID is assigned. 

    This can be useful for cloning schemas, but does not affect referenced objects. The 

    default value is y.


     

    大意就是说如果TRANSFORM参数设置成OID=N,表示在imp的时候,新创建的表或这个类型会赋予新的OID,而不是dmp文件中包含的OID的 值。但是这个参数的默认值是OID=Y,因此在进行Imp的时候,新创建的表或者type会赋予同样的OID,如果是位于同一个数据库上的不同 schema,那就会造成OID冲突的问题,因此解决这个问题也很简单,只需要在impdp的时候,显示设置transform. 参数为OID=N既可以了。如下所示,

    (cmd> impdp test/test directory=dump_dir dumpfile=dump.dmp logfile=dump.log remap_schema=frank:test  transform=OID:N)




  • Selenium 2.21 works with Python 3.2

    2012-05-09 16:07:35

    很久没来记录点东西了,最近在研究最新版Python3.2上selenium能否正常工作的问题。
    因为我打算用Python3.2+Selenium WebDriver写点东西,实现工作上的一些自动化测试。
     
    今天要分享的主要是如何解决一个在python3.2上经常会遇到的问题,只要是首次尝试的话,通常都会遇到.
     
    问题描述:
    执行简单的脚本,如打开一个网站,输入登录用户名或查询字符等,报如下的错误:
    “POST data should be bytes or an iterable of bytes. It cannot be str.”
     
    这个问题出现的原因主要是因为当前的WebDriver的selenium部分可能是基于Python2.x开发的,具体是哪个版本本人不确定.当你的Python升级到3.x时候,所以问题自然就来了,因为在3.x中python是有一些变化的,如2.x的selenium库里的一些函数的参数类型则在3.x的环境里发生了变化,这里报错就是因为参数类型从str变成了byte.至于有哪些change,建议自己去学习一下,这里不做赘述。
     
    当出现这个问题,可以使用如下的脚本文件去转换selenium webdriver并重新导入到Python安装目录下的相应路径中,如:C:\Python32\Lib\site-packages
     
    解压附件,得到pyselenium.py文件,用其执行相关selenium webdriver的转换工作.
    具体用法参考如下:
    pyselenium.py "C:\downloads\selenium-2.21.0.tar.gz"
     
    转换之后,相信webdriver就可以在python3.x上正常干活了!!!
     
    注:本贴中所用的附件来自于selenium 论坛的
     
     
  • 转:保持工作热情的一点看法

    2011-05-25 11:12:24

    迈入工作10年生涯,有团队邀请我结合实际案例简单聊聊"工作热情",附上我自己的理解,希望能对大家有所帮助。像很多成长课程一样,本文也是知易行难,实践是消化的唯一之道。

    一 热情的正反表现

    正:

    冲劲十足,执行力强
    自我驱动、要性强
    坚持如初,有感染力、激情四射

    反:

    被动接受任务,分外工作
    安于现状
    冷却,平淡


    你身边的榜样特质又是怎么样的呢?

    二热情来源

    1做事获取成就感,收获成长,自我鼓励、他人肯定,长期正反馈
    2做自己喜欢做并且擅长的事情
    3挖掘工作中乐趣,保持好奇心、新鲜感,了解底层原理、实现细节
    4适当危机感,持续高标准自我要求,把事情做到极致
    5明确阶段性目标,把握节奏,好心态

    三 热情消退的方式

    1经常性挫折,心累,很有无力感
    2缺乏反馈,不受重视
    3单纯重复性、事务性工作
    4目标过高或者安于现状,自我定位过高,过多比较、计较心

    四保持热情的一些做法

    和上述消退呼应。

    1 不逃避问题,寻求支持,借用团队力量,点滴进步、沉淀到量变导致质变
    2从不同角色、角度获取尽早反馈,管理上司
    3 换角度思考、思维创新,交流碰撞,开拓思路,反思深度、广度是否够
    4目标跳一跳就够着,分解到可执行,调整心态,做好持久战准备
    5适当自我调节,他人低谷
    6 重复N次变成习惯

  • 关于Python的super用法研究

    2011-04-08 12:11:23

    转自:http://hi.baidu.com/isno/blog/item/32899358e2a6a4d99d820419.html

    关于Python的super用法研究
    2008-11-09 10:38

    一、问题的发现与提出

    在Python类的方法(method)中,要调用父类的某个方法,在Python 2.2以前,通常的写法如代码段1:

    代码段1:

    class A:
    def __init__(self):
       print "enter A"
       print "leave A"

    class B(A):
    def __init__(self):
       print "enter B"
       A.__init__(self)
       print "leave B"

    >>> b = B()

    enter B
    enter A
    leave A
    leave B

    即,使用非绑定的类方法(用类名来引用的方法),并在参数列表中,引入待绑定的对象(self),从而达到调用父类的目的。

    这样做的缺点是,当一个子类的父类发生变化时(如类B的父类由A变为C时),必须遍历整个类定义,把所有的通过非绑定的方法的类名全部替换过来,例如代码段2,

    代码段2:

    class B(C):    # A --> C
    def __init__(self):
       print "enter B"
       C.__init__(self) # A --> C
       print "leave B"

    如果代码简单,这样的改动或许还可以接受。但如果代码量庞大,这样的修改可能是灾难性的。

    因此,自Python 2.2开始,Python添加了一个关键字super,来解决这个问题。下面是Python 2.3的官方文档说明:

    super(type[, object-or-type])

    Return the superclass of type. If the second argument is omitted the super object
    returned is unbound. If the second argument is an object, isinstance(obj, type)
    must be true. If the second argument is a type, issubclass(type2, type) must be
    true. super() only works for new-style. classes.

    A typical use for calling a cooperative superclass method is:

       class C(B):
           def meth(self, arg):
               super(C, self).meth(arg)

    New in version 2.2.

    从说明来看,可以把类B改写如代码段3:

    代码段3:

    class A(object):    # A must be new-style. class
    def __init__(self):
       print "enter A"
       print "leave A"

    class B(C):     # A --> C
    def __init__(self):
       print "enter B"
       super(B, self).__init__()
       print "leave B"

    尝试执行上面同样的代码,结果一致,但修改的代码只有一处,把代码的维护量降到最低,是一个不错的用法。因此在我们的开发过程中,super关键字被大量使用,而且一直表现良好。

    在我们的印象中,对于super(B, self).__init__()是这样理解的:super(B, self)首先找到B的父类(就是类A),然后把类B的对象self转换为类A的对象(通过某种方式,一直没有考究是什么方式,惭愧),然后“被转换”的类A对象调用自己的__init__函数。考虑到super中只有指明子类的机制,因此,在多继承的类定义中,通常我们保留使用类似代码段1的方法。

    有一天某同事设计了一个相对复杂的类体系结构(我们先不要管这个类体系设计得是否合理,仅把这个例子作为一个题目来研究就好),代码如代码段4:

    代码段4:

    class A(object):
    def __init__(self):
       print "enter A"
       print "leave A"

    class B(object):
    def __init__(self):
       print "enter B"
       print "leave B"

    class C(A):
    def __init__(self):
       print "enter C"
       super(C, self).__init__()
       print "leave C"

    class D(A):
    def __init__(self):
       print "enter D"
       super(D, self).__init__()
       print "leave D"
    class E(B, C):
    def __init__(self):
       print "enter E"
       B.__init__(self)
       C.__init__(self)
       print "leave E"

    class F(E, D):
    def __init__(self):
       print "enter F"
       E.__init__(self)
       D.__init__(self)
       print "leave F"

    >>> f = F()

    enter F
    enter E
    enter B
    leave B
    enter C
    enter D
    enter A
    leave A
    leave D
    leave C
    leave E
    enter D
    enter A
    leave A
    leave D
    leave F
    明显地,类A和类D的初始化函数被重复调用了2次,这并不是我们所期望的结果!我们所期望的结果是最多只有类A的初始化函数被调用2次——其实这是多继承的类体系必须面对的问题。我们把代码段4的类体系画出来,如下图:

        object
       |       \
       |        A
       |      / |
       B C D
        \   /   |
          E    |
            \   |
              F

    按我们对super的理解,从图中可以看出,在调用类C的初始化函数时,应该是调用类A的初始化函数,但事实上却调用了类D的初始化函数。好一个诡异的问题!

    二、走进Python的源码世界

    我们尝试改写代码段4中的函数调用,但都没有得到我们想要的结果,这不得不使我们开始怀疑:我们对super的理解是否出了问题。

    我们重新阅读了Python的官方文档,正如您所见,官方文档并没有详细的原理说明。到网络上去搜索,确实有人发现了同样的问题,并在一些论坛中讨论,但似乎并没有实质性的解答。既然,没有前人的足迹,我们只好走进Python的源码世界,去追溯问题的根源。

    我们考查的是Python 2.3的源码(估计Python 2.4的源码可能也差不多)。首先,搜索关键字"super"。唯一找到的是bltinmodule.c中的一句:

    SETBUILTIN("super", &PySuper_Type);

    于是,我们有了对super的第一个误解:super并非是一个函数,而是一个类(PySuper_Type)。

    在typeobject.c中找到了PySuper_Type的定义:

    代码段5:

    PyTypeObject PySuper_Type = {
    PyObject_HEAD_INIT(&PyType_Type)
    0,     /* ob_size */
    "super",    /* tp_name */
    sizeof(superobject),   /* tp_basicsize */
    0,     /* tp_itemsize */
    /* methods */
    super_dealloc,     /* tp_dealloc */
    0,     /* tp_print */
    0,     /* tp_getattr */
    0,     /* tp_setattr */
    0,     /* tp_compare */
    super_repr,    /* tp_repr */
    0,     /* tp_as_number */
    0,     /* tp_as_sequence */
    0,            /* tp_as_mapping */
    0,     /* tp_hash */
    0,     /* tp_call */
    0,     /* tp_str */
    super_getattro,    /* tp_getattro */
    0,     /* tp_setattro */
    0,     /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
       Py_TPFLAGS_BASETYPE, /* tp_flags */
       super_doc,    /* tp_doc */
       super_traverse,    /* tp_traverse */
       0,     /* tp_clear */
    0,     /* tp_richcompare */
    0,     /* tp_weaklistoffset */
    0,     /* tp_iter */
    0,     /* tp_iternext */
    0,     /* tp_methods */
    super_members,    /* tp_members */
    0,     /* tp_getset */
    0,     /* tp_base */
    0,     /* tp_dict */
    super_descr_get,   /* tp_descr_get */
    0,     /* tp_descr_set */
    0,     /* tp_dictoffset */
    super_init,    /* tp_init */
    PyType_GenericAlloc,   /* tp_alloc */
    PyType_GenericNew,   /* tp_new */
    PyObject_GC_Del,          /* tp_free */
    };
    从代码段5中可以得知,super类只改写了几个方法,最主要的包括:tp_dealloc,tp_getattro,tp_traverse,tp_init。

    再看superobject的定义:

    代码段6:

    typedef struct {
    PyObject_HEAD
    PyTypeObject *type;
    PyObject *obj;
    PyTypeObject *obj_type;
    } superobject;
    从代码段6中可以看到superobject的数据成员仅有3个指针(3个对象的引用)。要知道这3个对象分别代表什么,则必需考查super_init的定义:

    代码段7:

    static int
    super_init(PyObject *self, PyObject *args, PyObject *kwds)
    {
    superobject *su = (superobject *)self;
    PyTypeObject *type;
    PyObject *obj = NULL;
    PyTypeObject *obj_type = NULL;

    if (!PyArg_ParseTuple(args, "O!|O:super", &PyType_Type, &type, &obj))
       return -1;
    if (obj == Py_None)
       bj = NULL;
    if (obj != NULL) {
       obj_type = supercheck(type, obj);
       if (obj_type == NULL)
        return -1;
       Py_INCREF(obj);
    }
    Py_INCREF(type);
    su->type = type;
    su->obj = obj;
    su->obj_type = obj_type;
    return 0;
    }

    从代码中可以看到,super_init首先通过PyArg_ParseTuple把传入的参数列表解释出来,分别放在type和obj变量之中。然后通过supercheck测试可选参数obj是否合法,并获得实例obj的具体类类型。最后,把type, obj和obj_type记录下来。也就是说,super对象只是简单作了一些记录,并没有作任何转换操作。

    查找问题的切入点是为什么在类C中的super调用会切换到类D的初始化函数。于是在super_init中添加条件断点,并跟踪其后的Python代码。最终进入到super_getattro函数——对应于super对象访问名字__init__时的搜索操作。

    代码段8(省略部分无关代码,并加入一些注释):

    static PyObject *
    super_getattro(PyObject *self, PyObject *name)
    {
    superobject *su = (superobject *)self;
    int skip = su->obj_type == NULL;
    ……
    if (!skip) {
       PyObject *mro, *res, *tmp, *dict;
       PyTypeObject *starttype;
       descrgetfunc f;
       int i, n;

       starttype = su->obj_type; // 获得搜索的起点:super对象的obj_type
       mro = starttype->tp_mro; // 获得类的mro
       ……
       for (i = 0; i < n; i++) { // 搜索mro中,定位mro中的type
        if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i))
         break;
       }
       i++;       // 切换到mro中的下一个类
       res = NULL;
       for (; i < n; i++) {   // 在mro以后的各个命名空间中搜索指定名字
        tmp = PyTuple_GET_ITEM(mro, i);
        if (PyType_Check(tmp))
         dict = ((PyTypeObject *)tmp)->tp_dict;
        else if (PyClass_Check(tmp))
         dict = ((PyClassObject *)tmp)->cl_dict;
        else
         continue;
        res = PyDict_GetItem(dict, name);
        if (res != NULL) {
         Py_INCREF(res);
         f = res->ob_type->tp_descr_get;
         if (f != NULL) {
          tmp = f(res, su->obj,
           (PyObject *)starttype);
          Py_DECREF(res);
          res = tmp;
         }
         return res;
        }
       }
    }
    return PyObject_GenericGetAttr(self, name);
    }
    从代码中可以看出,super对象在搜索命名空间时,其实是基于类实例的mro进行。那么什么是mro呢?查找官方文档,有:

    PyObject* tp_mro
    Tuple containing the expanded set of base types, starting with the type itself and
    ending with object, in Method Resolution Order.

    This field is not inherited; it is calculated fresh by PyType_Ready().

    也就是说,mro中记录了一个类的所有基类的类类型序列。查看mro的记录,发觉包含7个元素,7个类名分别为:

    F E B C D A object

    从而说明了为什么在C.__init__中使用super(C, self).__init__()会调用类D的初始化函数了。

    我们把代码段4改写为:

    代码段9:

    class A(object):
    def __init__(self):
       print "enter A"
       super(A, self).__init__() # new
       print "leave A"

    class B(object):
    def __init__(self):
       print "enter B"
       super(B, self).__init__() # new
       print "leave B"

    class C(A):
    def __init__(self):
       print "enter C"
       super(C, self).__init__()
       print "leave C"

    class D(A):
    def __init__(self):
       print "enter D"
       super(D, self).__init__()
       print "leave D"
    class E(B, C):
    def __init__(self):
       print "enter E"
       super(E, self).__init__() # change
       print "leave E"

    class F(E, D):
    def __init__(self):
       print "enter F"
       super(F, self).__init__() # change
       print "leave F"

    >>> f = F()

    enter F
    enter E
    enter B
    enter C
    enter D
    enter A
    leave A
    leave D
    leave C
    leave B
    leave E
    leave F
    明显地,F的初始化不仅完成了所有的父类的调用,而且保证了每一个父类的初始化函数只调用一次。

    三、延续的讨论

    我们再重新看上面的类体系图,如果把每一个类看作图的一个节点,每一个从子类到父类的直接继承关系看作一条有向边,那么该体系图将变为一个有向图。不能发现mro的顺序正好是该有向图的一个拓扑排序序列。

    从而,我们得到了另一个结果——Python是如何去处理多继承。支持多继承的传统的面向对象程序语言(如C++)是通过虚拟继承的方式去实现多继承中父类的构造函数被多次调用的问题,而Python则通过mro的方式去处理。

    但这给我们一个难题:对于提供类体系的编写者来说,他不知道使用者会怎么使用他的类体系,也就是说,不正确的后续类,可能会导致原有类体系的错误,而且这样的错误非常隐蔽的,也难于发现。

    四、小结

    1. super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,
           产生了一个super对象;
    2. super类的初始化函数并没有做什么特殊的操作,只是简单记录了类类型和具体实例;
    3. super(B, self).func的调用并不是用于调用当前类的父类的func函数;
    4. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数
           只调用一次(如果每个类都使用super);
    5. 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一
           个父类函数被调用多次。

  • 修改虚拟机的物理地址

    2011-03-23 16:47:51

    最实用,快速的更改方法:

    右击 我的电脑->Device Manager->Network adapters->选中某网卡,右击属性->Advanced->Locally Administered Address-> 输入想要设定的mac value->OK

    转:

    1、修改虚拟机的*.vmx文件.

    这种方法最值得推荐,因为这样就类似于重新“烧录”了VMware虚拟机的“物理网卡ROM”。方法是:

    分两种情况:

    a:

    ethernet0.addressType = "static"

    ethernet0.Address = "00:50:56:0A:0B:0C"

    "static"说明VM的"物理网卡"的MAC是静态设定的,你可以改成一个以005056开头的另外一个MAC即可。改完启动VM时如果问你SSID的话,选择“Keep Always”。

    b:

    ethernet0.addressType = "generated"

    uuid.location = "56 4d dc f1 ff aa 75 ea-f1 b9 ee 0d 68 9c 65 5c"

    uuid.bios = "56 4d ed 23 13 8c 96 91-7c 68 b2 09 8b aa bb cc"

    ethernet0.generatedAddress = "00:0c:29:aa:bb:cc"

    "generated"说明VM的"物理网卡"的MAC是系统随机动态设定的,你可以通过将uuid.bios后六位及ethernet0.generatedAddress后六位改成你想要改成的以000c29开头的MAC即可。

     

    2、修改Linux系统里相关 /etc/sysconfig/network-scripts/ifcfg-eth0文件MAC值.

    vi /etc/sysconfig/network-scripts/ifcfg-eth0

    MACADDR=xx:xx:xx:xx:xx:xx

    :wq 保存退出

    reboot

     

    3、修改Linux系统里相关rc.local文件MAC值.

    可以通过改启动脚本/etc/rc.d/rc.local:

    ifconfig eth0 down

    ifconfig eth0 hw ether xxxxxxxxxxxx

    ifconfig eth0 up

    /sbin/route add default gw x.x.x.x eth0

    :wq 保存退出

    reboot

  • web测试小结

    2011-03-17 17:09:02

    从事web测试近2年了,记录下目前想到的一些测试点:

    GUI;

    Function;

    Performance;

    Security

  • 培养总结的习惯

    2010-04-28 17:15:35

      一起毕业的大学同学,几年后再度相聚,有的成长为leader, manager,是名副其实的白领;有的,不温不火,一直在公司“基层”徘徊,更有的还最终因为不能胜任乃至转行,为谋生计痛苦不已。为什么有如此之大的差别?相信原因有很多,天时、地利、人和……

      突然想起刚毕业时的那段时光,我想,是否具备良好的工作习惯是一个非常重要的原因,今天,想跟大家在回忆过去的同时, 谈一谈:你,善于“总结”吗?这点认识得益于我的一位前辈。

      那时,我们team正从上至下整个“大换血”,“换血”后我们这批刚从大学毕业的什么都不懂却血气方刚的学生, 以及新任的“ 空降” 副总( 兼职管理我们team)组成了一个新的团队。

      于是,“空降”副总的工作开始变成了:小到手把手的教导我们怎么给客户倒咖啡, 写ma i l 如何注意语法; 大到如何跟p r o j e c tmanager 交流……当时觉得不能理解,一个副总总教导我们这些个“芝麻绿豆”的事情。随后team又调来了一位有丰富工作经验的师姐,很多原先副总亲力亲为的事情都转交给她,起初还好,可时间久了,大家渐渐开始不太愿意跟她交流,倒不是她不乐意教我们这帮毛毛头,而是大家有点不习惯她说话的语气语调,总觉得在接受强硬的上纲上线教育,心里挺不自在。我的座位跟师姐毗邻,免不了要经常交流;渐渐的我发现她总在结束一天工作前静静的写一些东西,觉得好奇,就随口问了一次,她笑笑告诉说:“我要把今天做的事情总结一下,这对我以后的工作很有用。。。”听完,我心里还有些不屑:有必要吗?每天都要如此,累不累啊?

      我们每周都会通过weekly meeting 来review工作进度,内容大致划分为:问题列举、解决方案、工作心得以及下周工作计划......因为这个会议都由manager直接负责,当我们自我感觉良好、很不虚心的说出一周来的心得时,manager都会紧跟着泼一盆凉水或是严厉的指责、批评。所以,总是在委屈不服的同时又不得不回过头来想:确实,这一点我没想到;确实这样做会更好;确实......而此时,只有师姐一个人准备的很充分,虽然也会偶尔被批评,但我们都听出来了,那是欣慰中的点拨。Manager倡导我们多向她学习。慢慢的,大家开始逼迫着自己去总结,去反省。]

      于是,我们几乎都是在这样的骂声中成长着,每个人在日常的工作中带着随时总结的心态去工作,及时的更新相关知识,工作流程得到改进,更多的问题迎刃而解,最后终于在不知不觉中养成了总结的习惯,我们的“客户”- Project Manager在project meeting中从原先对我们的不屑,到后来变成了一句:进步很大,越来越professional, 强将手下无弱兵啊!

      虽然成长的过程是痛苦的,但当回忆起过去的这段经历,我很欣慰!相信,当时团队中的每个人都难忘这段经历!孔子曰:“吾日三省其身”,很有道理,一个善于总结的个人和组织才会持续进步。

      希望我们都能够克服人性的懒惰, 坚持不断的总结,Keep Improving ...

     
  • JMeter研究笔记

    2010-03-30 16:58:59


    JMeter Jakarta Users Test Plan组成及设置详解:

    Thread Group:
    Add --> ThreadGroup
    Number of threads(users), 该属性模拟虚拟用户数。
    Ramp-Up Period(in seconds), 启动所有用户的时间。eg:设置Ramp-Up period为5秒,则JMeter将在5秒内启动所有的用户。因此,当用户数为5时,因此启动每个用户的时间间隔应为:5 users / 5 seconds = 1 user per second. 如果该属性设为0,则JMeter将立即启动所有用户。
    Loop Count,即重复测试的次数。 如果设为1, 则JMeter仅运行测试1次。如果需要重复运行Test Plan,则需选择“Forever”选项。

    HTTP Request Defaults:
    Add --> Config Element --> HTTP Request Defaults
    设置HTTP requests的默认设置,HTTP Request功能将会使用这里设置的默认项。
    Name, 该项通常保留其默认值,
    Web Server's Server Name or IP,这里设定的Server Name或者IP是Test Plan中所有HTTP请求的目标。即所有的HTTP请求都会被送往这里设定的Web Server。

    HTTP Cookie Manager:
    Add --> Config Element --> HTTP Cookie Manager

    HTTP Requests:
    提示:HTTP Requests的执行顺序以在Test Plan树种显示的为准。
    Add --> Sampler --> HTTP Request
    设置HTTP Request, 修改Name, 设置Path,若访问的是主页,通常设为"/". 其他则不用修改,因为Server Name等已在HTTP Request Defaults中设置完毕。
    如果需要添加其他HTTP Request访问其他页面,则需再次添加HTTP Request,并修改Name以及Path。以jakrata为例:访问Guidelines页面,添加HTTP Request后,修改Name为:"Project Guidelines", Path设置为:"/site/guidelines.html".
    测试其他应用时,以此为参考。


    高级Test Plan的创建
    用户session以及URL重写的处理:
    URL Re-writing Modifier被添加在哪个控件下,则URL Re-writing只影响该控件下的requests.

    HTTP Header Manager
    HTTP Header Manager提供让用户定制JMeter发送的HTTP requests header的功能。
    HTTP Request Header包含的属性有:"User-Agent","Pragma","Referer",etc.
    HTTP Header Manager跟HTTP Cookie Manager一样,通常都是添加在Thread Group下.


    创建一个数据库Test Plan:
    1.添加一个Thread Group,命名为:JDBC Users, 添加虚拟用户数:10,Ramp-Up period: 0, 循环次数:3
    2.添加JDBC request, Add --> Config Element --> JDBC Connection Configuration,
    Variable Name Bound to Pool, 通常用于JDBC Sampler 并唯一标示被使用的配置。这里不做改动,默认留空即可。
    Connection Pool Configuration, 不做改动,默认
    Connection Validation by Pool, 不做改动,默认
    Database Connection Configuration下设置,DB URL,如:jdbc:mysql://localhost:3306/test
    JDBC Driver class, 如:com.mysql.jdbc.Driver, 用户名,密码即可。
    JMeter将使用JDBC connection 控制面板中的信息创建数据库连接池。几个不同的JDBC连接都可以被使用,但是他们必须有唯一的名称。每个JDBC Request对应一个JDBC配置池。
    3.添加JDBC Request,Add --> Sampler --> JDBC Request,修改Name,Pool Name改为:MySQL(跟配置元素一样),输入SQL查询语句。可以根据需要再次添加JDBC Request,与上步骤同。


    FTP Test Plan:
    http://jakarta.apache.org/jmeter/usermanual/build-ftp-test-plan.html
    1. Add --> ThreadGroup. 同时设置用户数,启动用户时间间隔总数以及循环次数。
    2. Add --> Config Element --> FTP Request Defaults,Name通常保留不做改动。Server Name/IP为所有FTP请求的目标。
    3. Add --> Sampler --> FTP Request,修改Name,Server/IP保留不做修改,因为在FTP Request Defaults里已经设置。File to Retrieve From Server,填写文件再ftp服务器上的路径,如:pub/msql/java/tutorial.txt,Login下填写相应的用户名密码。

  • 【转】Linux/Unix 性能分析命令输出说明

    2010-02-08 16:24:59

    1,CPU使用情况分析

    vmstat 命令获得汇总信息.
    有两个参数:1,每行输出需监视系统的秒数.2,提供的报告数.如果没有提供指定报告的行数,vmstat会一直运行.直到按下<control+c>时为止.
    vmstat返回的第一行数据提供了自系统引导起来以后的平均值.随后的个行是再上一个采样期内的平均值.默认的采用时间为5秒.



    $ vmstat 5 5
    procs memory swap io system cpu
    r b swpd free buff cache si so bi bo in cs us sy id wa
    1 1 4 172944 248936 5954084 0 0 53 33 86 206 7 5 85 3
    0 1 4 172932 248936 5954140 0 0 1561 50 1135 5024 4 6 45 45
    2 1 4 172844 248936 5954296 0 0 2132 144 1465 6612 26 8 33 33
    1 1 4 172716 248936 5954704 0 0 3140 194 1845 9565 26 8 36 30
    1 1 4 172364 248936 5955512 0 0 1963 1062 1329 6709 24 8 29 39

    r
    b

    swpd
    free 空闲内存
    buff (缓冲器,)
    cache 缓存使用内存

    si
    so

    io
    bo

    cs 每个时间段上下文切换的次数,也就是由内核切换当前运行进程的次数
    in 每隔时间间隔内的中断数。cs或in的数值极高一般标识应将设备或运行有错误

    us 用户时间 数值较大表示计算机处于运算状态
    sy 系统时间 数值较大表示进程正在做大量系统调用或执行I/O操作
    id 空闲时间 一种粗劣规则是系统中50%的非空闲时间将用于用户空间,而另外50%用于系统时间;同时总统的空闲时间百分
    比不应该为0.

    Mpstat用于调试SMP(sysmetric multiprocessing.对称多处理器).-P参数可以指定一个要给出报告的特定处理器.
    [tapeback@xlback bin]$ mpstat 1 5
    Linux 2.6.9-22.ELsmp (xlback.rrl.com) 09/20/2006

    05:45:16 PM CPU %user %nice %system %iowait %irq %soft %idle intr/s
    05:45:17 PM all 0.00 0.00 0.00 0.00 0.00 0.00 100.00 1029.00
    05:45:18 PM all 0.50 0.00 0.00 0.00 0.00 0.00 99.50 1032.00
    05:45:19 PM all 4.02 0.00 0.00 0.00 0.00 0.00 95.98 1009.80
    05:45:20 PM all 0.00 0.00 0.00 0.00 0.00 0.00 100.00 1017.00
    05:45:21 PM all 0.00 0.00 0.00 0.00 0.00 0.00 100.00 1019.00
    Average: all 0.90 0.00 0.00 0.00 0.00 0.00 99.10 1021.31


    uptime命令获得负载的平均值.平均负载包括等待磁盘核网络I/O的进程,它并不是CPU使用情况的纯粹指标.
    % uptime
    14:05:05 up 112 days, 22:37, 5 users, load average: 1.84, 1.81, 1.33
    给出的3个数值分别对应系统在5分钟,10分钟和15分钟的平均负载,
    Linux系统在平均负载达到3的时候就处于繁忙状态.而且不能很好地处理平均负载超过6的情况.


    [tapeback@xlback bin]$ ps -aux
    Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.3/FAQ
    USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
    root 1 0.0 0.0 4748 548 ? S Aug22 0:00 init [2]
    root 2 0.0 0.0 0 0 ? S Aug22 0:00 [migration/0]
    root 3 0.0 0.0 0 0 ? SN Aug22 0:00 [ksoftirqd/0]
    root 4 0.0 0.0 0 0 ? S Aug22 0:00 [migration/1]
    root 5 0.0 0.0 0 0 ? SN Aug22 0:00 [ksoftirqd/1]
    root 6 0.0 0.0 0 0 ? S< Aug22 0:00 [events/0]
    root 7 0.0 0.0 0 0 ? S< Aug22 0:00 [events/1]
    root 8 0.0 0.0 0 0 ? S< Aug22 0:00 [khelper]
    root 9 0.0 0.0 0 0 ? S< Aug22 0:00 [kacpid]
    root 42 0.0 0.0 0 0 ? S< Aug22 0:00 [kblockd/0]
    root 43 0.0 0.0 0 0 ? S< Aug22 0:00 [kblockd/1]
    root 59 0.0 0.0 0 0 ? S< Aug22 0:00 [aio/0]
    root 60 0.0 0.0 0 0 ? S< Aug22 0:00 [aio/1]
    root 44 0.0 0.0 0 0 ? S Aug22 0:00 [khubd]
    root 58 0.0 0.0 0 0 ? S Aug22 0:03 [kswapd0]
    root 133 0.0 0.0 0 0 ? S Aug22 0:00 [kseriod]
    root 204 0.0 0.0 0 0 ? S Aug22 0:00 [scsi_eh_0]
    root 217 0.0 0.0 0 0 ? S Aug22 0:00 [scsi_eh_1]
    root 218 0.0 0.0 0 0 ? S Aug22 0:00 [ahc_dv_0]
    root 242 0.0 0.0 0 0 ? S Aug22 0:00 [scsi_eh_2]
    root 243 0.0 0.0 0 0 ? S Aug22 0:00 [ahc_dv_1]
    root 250 0.0 0.0 0 0 ? S Aug22 0:06 [kjournald]

    USER 进程属主的用户名
    PID 进程ID
    %CPU 该进程正在使用的CPU时间百分数
    %MEM 该进程正在使用的实际内存的百分数
    VSZ 进程的虚拟大小
    RSS 驻留集的大小(内存中页的数量)
    TTY 控制终端的ID
    STAT 当前进程的状态:
    R=可运行 D=在等待磁盘(或者短期等待)
    S=在睡眠(<20秒) T=被跟踪或者被停止
    Z=僵进程
    附加标志:
    W=进程被交换出去
    <=进程拥有比普通优先级更高的优先级
    N=进程拥有比普通优先级更低的优先级
    L=有些页面被缩在内存中
    START 启动进程的时间
    TIME 进程已经消耗掉的CPU时间
    COMMAND 命令的名称和参数


    [tapeback@xlback bin]$ top
    top - 17:47:49 up 29 days, 17:27, 1 user, load average: 0.17, 0.43, 0.36
    Tasks: 92 total, 1 running, 85 sleeping, 0 stopped, 6 zombie
    Cpu(s): 0.0% us, 0.2% sy, 0.0% ni, 99.8% id, 0.0% wa, 0.0% hi, 0.0% si
    Mem: 2056208k total, 1977416k used, 78792k free, 60544k buffers
    Swap: 2097144k total, 668544k used, 1428600k free, 1597712k cached

    PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
    19807 tapeback 16 0 6144 1000 768 R 0.3 0.0 0:00.07 top
    1 root 16 0 4748 548 456 S 0.0 0.0 0:00.53 init
    2 root RT 0 0 0 0 S 0.0 0.0 0:00.31 migration/0
    3 root 34 19 0 0 0 S 0.0 0.0 0:00.00 ksoftirqd/0
    4 root RT 0 0 0 0 S 0.0 0.0 0:00.50 migration/1
    5 root 34 19 0 0 0 S 0.0 0.0 0:00.02 ksoftirqd/1
    6 root 5 -10 0 0 0 S 0.0 0.0 0:00.03 events/0
    7 root 5 -10 0 0 0 S 0.0 0.0 0:00.04 events/1
    8 root 8 -10 0 0 0 S 0.0 0.0 0:00.00 khelper


    第一行的项目依次为当前时间、系统启动时间、当前系统登录用户数目、平均负载。

    第二行为进程情况,依次为进程总数、运行进程数、休眠进程数、僵死进程数、终止进程数。

    第三行为CPU状态,依次为用户占用、系统占用、优先任务占用、闲置任务占用。

    第四行为内存状态,依次为平均可用内存、已用内存、空闲内存、缓存使用内存。

    第五行为交换状态,依次为平均可用交换容量、已用容量、闲置容量、交换高速缓存容量。

    PID 进程ID
    USER 进程属主的用户名
    PR
    NI
    VIRT
    RES
    SHR
    S
    %CPU 该进程正在使用的CPU时间百分数
    %MEM 该进程正在使用的实际内存的百分数
    TIME 进程已经消耗掉的CPU时间
    COMMAND 命令的名称和参数

    sar的语法如下:
    sar [-options] [interval [count]]
    其中,internal是两次采样的间隔时间;count是指采样的次数;与CPU相关的options有:

    参数的含义如下:

    -c 表示输出采用的时间
    -e hh:mm:ss 表示只显示CPU的信息
    -i {irq |SUM|ALL|XALL} 相邻的两次采样的间隔时间
    -P {cpu|ALL}
    -q 显示在采样的时刻,可运行队列的任务的个数,以及系统平均负载
    -u CPU 使用的情况,报告了cpu的用户态,系统态,等待I/O和空闲时间上的百分比。
    -w: 每秒上下文交换率
    -o: filename 将结果放在文件里
    -f: filename 表示从file文件中取出数据,如果没有指定-f file,则从标准数据文件

    sar -c 2 -q 2 -u -w
    Linux 2.6.9-22.ELsmp (xlback.rrl.com) 09/20/2006

    07:23:48 PM proc/s
    07:23:50 PM 0.00

    07:23:48 PM cswch/s
    07:23:50 PM 325.87

    07:23:48 PM CPU %user %nice %system %iowait %idle
    07:23:50 PM all 0.00 0.25 0.00 0.00 99.75

    07:23:48 PM runq-sz plist-sz ldavg-1 ldavg-5 ldavg-15
    07:23:50 PM 0 113 0.05 0.20 0.14

    与CPU有关的输出的含义

    参数 解释 从/proc/stat获得数据
    proc/s 在internal时间段里,每秒上下文切换率 processes/total*100
    cswch 在internal时间段里,每秒上下文切换率 ctxt/total*100
    intr/s 在internal时间段里,每秒CPU接收的中断的次数 idle/total*100

    从/proc/loadavg获得数据
    runq-sz 采样时,运行队列中任务的个数,不包括vmstat 进程。 procs_running-1
    plist-sz 采样时,系统中活跃的任务的个数 nr_threads
    ldavg-1 采样的前一秒钟系统的负载(%) lavg_1
    ldavg-5 采样的5秒钟系统的负载(%) lavg_5
    ldavg-15 采样的前15秒钟系统的负载(%) lavg_15

    sar 1 10
    Linux 2.6.9-22.ELsmp (xxx) 09/20/2006

    06:54:52 PM CPU %user %nice %system %iowait %idle
    06:54:53 PM all 0.50 0.00 0.00 0.00 99.50
    06:54:54 PM all 1.00 0.00 0.50 0.00 98.50
    06:54:55 PM all 0.00 0.00 0.50 0.00 99.50
    06:54:56 PM all 0.00 0.00 0.00 0.00 100.00
    06:54:57 PM all 5.97 0.00 0.50 0.50 93.03
    06:54:58 PM all 0.00 0.00 0.50 0.00 99.50

    06:54:58 PM CPU %user %nice %system %iowait %idle
    06:54:59 PM all 0.50 0.00 0.00 0.00 99.50
    06:55:00 PM all 1.00 0.00 0.00 0.00 99.00
    06:55:01 PM all 0.00 0.00 0.00 0.00 100.00
    06:55:02 PM all 0.00 0.00 0.00 0.00 100.00
    Average: all 0.90 0.00 0.20 0.05 98.85

    user 在internal时间段里,用户态的CPU时间(%) ,不包含 nice值为负进程 usr/total*100
    nice 在internal时间段里,nice值为负进程的CPU时间(%) nice/total*100
    sys 在internal时间段里,核心时间(%) (system+irq+softirq)/total*100
    iowait 在internal时间段里,硬盘IO等待时间(%) iowait/total*100
    idle 在internal时间段里,CPU除去等待磁盘IO操作外的因为任何原因而空闲的时间闲置时间 (%) idle/total*100

    2,linux的内存管理.
    和unix一样,linux也是按照页的单元来管理内存的.目前在pc硬件上,页的大小为4kb.linux内核在进程需要内存的时候,分配给他们虚拟页,每
    个虚拟页都被眏射到实际存储器上.既RAM或者磁盘上的交换空间.LINUX使用一个"页表"(pagetable)"来跟踪这些虚拟页同实际页之间的眏射
    关系.Linux用交换空间(swapspace)来增加实际RAM的大小,有效地向进程提供它们所需要的内存.既然进程都以为他们的虚拟页眏射到了
    实际的内存上,所以Linux总是忙于在RAM和交换区之间来回换页,这种活动称为调页(paging).

    内存使用情况分析.
    内存活动基本上可以用3个数字来量化:活动虚拟内存总量,交换(swapping)率和调页(paging)率.其中第一个数字表明内存的总需求量,后两个
    数字表示那些内存中有多少比例正处在使用之中.目标是减少内存活动或增加内存量,直到调页率保持在一个可以接受的水平上为止.
    使用free命令来判断当前投入使用的内存和交换的数量.带-t标志执行这条命令会自动计算出虚拟内存的总量.
    free -t
    total used free shared buffers cached
    Mem: 2056208 1977736 78472 0 60552 1598180
    -/+ buffers/cache: 319004 1737204
    Swap: 2097144 668544 1428600
    Total: 4153352 2646280 1507072


    Swapon命令来准确地判断出正在那些文件和分区作为交换空间.

    Procinfo命令是把/proc下的文件已较好的格式显示出来.
    Procinfo -n5 能以5秒钟为间隔连续的属性输出结果.
    Procinfo输出的信息有一些和free , uptime 和 vmstat
    输出的信息重复了.此外,procinfo提供了有关内核版本,内存调页,磁盘访问以及IRQ分配的信息.可以使用procinfo -a
    看到/proc文件系统里的更多信息,其中包括内核的引导参数,内核的可以加载模块,字符设备和文件系统

    磁盘I/O分析
    使用iostat 命令监视磁盘的性能.
    $ iostat
    Linux 2.6.9-22.ELsmp (xxx) 09/20/2006

    avg-cpu: %user %nice %sys %iowait %idle
    1.79 0.05 0.29 2.96 94.91

    Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
    sda 0.61 1.12 15.81 2886882 40672114
    sdb 60.69 643.80 267.79 1656348001 688974012
    sdc 0.00 0.00 0.00 8527 0

    user 在internal时间段里,用户态的CPU时间(%) ,不包含 nice值为负进程 usr/total*100
    nice 在internal时间段里,nice值为负进程的CPU时间(%) nice/total*100
    sys 在internal时间段里,核心时间(%) (system+irq+softirq)/total*100
    iowait 在internal时间段里,硬盘IO等待时间(%) iowait/total*100
    idle 在internal时间段里,CPU除去等待磁盘IO操作外的因为任何原因而空闲的时间闲置时间 (%) idle/total*100

    tps 每秒的I/O传输次数
    Blk_read/s 每秒读取的块数
    Blk_wrtn/s 每秒写入的块数
    Blk_read 读取的总块数
    Blk_wrtn 写入的总块数

    总之:
    Cpu的检查工具有:vmstat mpstat -p uptime ps -aux
    Memory free -t swapon -s procinfo top
    磁盘 iostat

    另: sar 工具.

  • 【转载】七个关于有效沟通的哲理故事

    2010-01-28 10:56:13

    原创作者:Jerry
    转载请注明:来自Sawin系统分析之窗
    最后修改时间:2005-8-25

        有效沟通是企业经营管理和我们个人在社会生活经常需要遇到的基本问题。人与人之间要达成真正的沟通并不是一件易事。以下一些简洁而寓意深刻的故事,可能比我和其他沟通专家所著的专业文章对你更直接,更具有震憾作用和启发意义。

       1 有一个秀才去买柴,他对卖柴的人说:“荷薪者过来!”卖柴的人听不懂“荷薪者”(担柴的人)三个字,但是听得懂“过来”两个字,于是把柴担到秀才前面。

       秀才问他:“其价如何?”卖柴的人听不太懂这句话,但是听得懂“价”这个字,于是就告诉秀才价钱。 秀才接着说:“外实而内虚,烟多而焰少,请损之。(你的木材外表是干的,里头却是湿的,燃烧起来,会浓烟多而火焰小,请减些价钱吧。)”卖柴的人因为听不懂秀才的话,于是担着柴就走了。

       管理者平时最好用简单的语言、易懂的言词来传达讯息,而且对于说话的对象、时机要有所掌握,有时过分的修饰反而达不到想要完成的目的。

       2 美国知名主持人林克莱特一天访问一名小朋友,问他说:“你长大后想要当甚么呀?”小朋友天真的回答:“嗯…我要当飞机的驾驶员!”林克莱特接着问:“如果有一天,你的飞机飞到太平洋上空所有引擎都熄火了,你会怎么办?”小朋友想了想:“我会先告诉坐在飞机上的人绑好安全带,然后我挂上我的降落伞跳出去。”当在现场的观众笑的东倒西歪时,林克莱特继续着注视这孩子,想看他是不是自作聪明的家伙。没想到,接着孩子的两行热泪夺眶而出,这才使的林克莱特发觉这孩子的悲悯之情远非笔墨所能形容。于是林克莱特问他说:“为甚么要这么做?”小孩的答案透露出一个孩子真挚的想法: “我要去拿燃料,我还要回来!!

       你听到别人说话时……你真的听懂他说的意思吗?你懂吗?如果不懂,就请听别人说完吧,这就是“听的艺术”:1. 听话不要听一半。2. 不要把自己的意思,投射到别人所说的话上头。

       3 A对B说:"我要离开这个公司。我恨这个公司!"B建议道:"我举双手赞成你报复!!破公司一定要给它点颜色看看。不过你现在离开,还不是最好的时机。" A问:为什么?B说:"如果你现在走,公司的损失并不大。你应该趁着在公司的机会,拼命去为自己拉一些客户,成为公司独挡一面的人物,然后带着这些客户突然离开公司,公司才会受到重大损失,非常被动。"A觉得B说的非常在理。于是努力工作,事遂所愿,半年多的努力工作后,他有了许多的忠实客户。再见面时B问A:“现在是时机了,要跳赶快行动哦!”A淡然笑道:“老总跟我长谈过,准备升我做总经理助理,我暂时没有离开的打算了。”

       其实这也正是B的初衷。一个人的工作,只有付出大于得到,让老板真正看到你的能力大于位置,才会给你更多的机会替他创造更多利润。

       4 曾经有个小国的人到中国来,进贡了三个一模一样的金人,把皇帝高兴坏了。可是这小国的人不厚道,同时出一道题目:这三个金人哪个最有价值?皇帝想了许多办法,请来珠宝匠检查,称重量,看做工,都是一模一样的。

       怎么办?使者还等着回去汇报呢。泱泱大国,不会连这个小事都不懂吧?最后,有一位退位的老大臣说他有办法。皇帝将使者请到大殿,老臣胸有成足地拿着三根稻草,插入第一个金人的耳朵里,这稻草从另一边耳朵出来了。第二个金人的稻草从嘴巴里直接掉出来,而第三个金人,稻草进去后掉进了肚子,什么响动也没有。老臣说:第三个金人最有价值!使者默默无语,答案正确。 最有价值的人,不一定是最能说的人。老天给我们两只耳朵一个嘴巴,本来就是让我们多听少说的。善于倾听,才是成熟的人最基本的素质。

       5 有一位表演大师上场前,他的弟子告诉他鞋带松了。大师点头致谢,蹲下来仔细系好。等到弟子转身后,又蹲下来将鞋带解松。有个旁观者看到了这一切,不解地问:“大师,您为什么又要将鞋带解松呢?”大师回答道:“因为我饰演的是一位劳累的旅者,长途跋涉让他的鞋带松开,可以通过这个细节表现他的劳累憔悴。” “那你为什么不直接告诉你的弟子呢?”“他能细心地发现我的鞋带松了,并且热心地告诉我,我一定要保护他这种热情的积极性,及时地给他鼓励,至于为什么要将鞋带解开,将来会有更多的机会教他表演,可以下一次再说啊。”

       6 一个女儿对父亲抱怨她的生活,抱怨事事都那么艰难。她不知该如何应付生活,想要自暴自弃了。她已厌倦抗争和奋斗,好象一个问题刚解决,新的问题就又出现了。

       她的父亲是位厨师,他把她带进厨房。他先往三只锅里倒入一些水,然后把它们放在旺火上烧。不久锅里的水烧开了。他往一只锅里放些胡萝卜,第二只锅里放只鸡蛋,最后一只锅里放入碾成粉末状的咖啡豆。他将它们侵入开水中煮,一句话也没有说。

       女儿咂咂嘴,不耐烦地等待着,纳闷父亲在做什么。大约20分钟后,他把火闭了,把胡萝卜捞出来放入一个碗内,把鸡蛋捞出来放入另一个碗内,然后又把咖啡舀到一个杯子里。做完这些后,他才转过身问女儿,“亲爱的,你看见什么了?”“胡萝卜、鸡蛋、咖啡”,她回答。

       他让她靠近些并让她用手摸摸胡萝卜。她摸了摸,注意到他们变软了。父亲又让女儿拿一只鸡蛋并打破它。将壳剥掉后,他看到了是只煮熟的鸡蛋。最后,他让她喝了咖啡。品尝到香浓的咖啡,女儿笑了。她怯生问到:“父亲,这意味着什么?”

       他解释说,这三样东西面临同样的逆境——煮沸的开水,但其反应各不相同。胡萝卜入锅之前是强壮的,结实的,毫不示弱;但进入开水之后,它变软了,变弱了。鸡蛋原来是易碎的,它薄薄的外壳保护着它呈液体的内脏。但是经开水一煮,它的内脏变硬了。而粉状咖啡豆则很独特,进入沸水之后,它们倒改变了水。“哪个是你呢?”他问女儿。“当逆境找上门来时,你该如何反应?你是胡萝卜,是鸡蛋,还是咖啡豆?”

       7 有一个人因为生意失败,逼不得已变卖了新购的住宅,而且连他心爱的小跑车也脱了手,改以电单车代步。有一日,他和太太一起,相约了几对私交甚笃的夫妻出外游玩,其中一位朋友的新婚妻子因为不知详情,见到他们夫妇共乘一辆电单车来到约定地点,便冲口而出地问:「为甚么你们骑电单车来?」众人一时错愕,场面变得很尴尬,但这位妻子不急不缓地回应答:「我们骑电单车,因为我想抱着他。
  • 【转载】 软件测试自动化的现状分析和自动化引入条件

    2010-01-26 14:50:40

     软件测试自动化,已经成为国内软件工程领域一个众所周知的课题;不言而喻,软件测试从业者都意识到软件测试这项工作走向成熟化、标准化的一个必经之路就是要实施自动化测试。也许您认为实施自动化测试不是必须,也许您认为测试的思想是开展该工作的精髓、而工具只是辅助,那么我要告诉你我的想法:从计算机这一庞大学科发展至今,它最根本的意义是解决人类手工劳动的复杂性,成为替代人类某些重复性行为模式的最佳工具;我们不可推翻测试思维在测试工作中的指导思想地位,但如何将思想转化成可操作的方案,本文也许会给您一些启示。
      以前听过北京中软的一个业内专家讲一句话,觉得挺经典:凡是说既是科学又是艺术的学科,就是说明它是不成熟的学科!他将软件工程和建筑行业做类比,让我们深深体会到软件工程走向成熟化的任重与道远。而软件测试,更是一个新兴的领域,虽然近几年得到了快速发展,也随着该领域从业者数量的与日俱增,培养了一批高级的人才;但是依然有多少企业和个人工作在迷茫中:这种困惑是因为工程师们手中的测试工作与理想的测试模式造成的强烈反差,这种无奈是因为他们和开发人员一样的努力却有不同的待遇,这种迷茫是因为测试工作者不知道这个领域里是否还有自己的发展空间和人生价值的体现!笔者认为:如今的软件测试行情,正处在群雄逐鹿的混战岁月,每个人、每个有测试部门或从事测试业务的企业,都该发扬百花齐放、百家争鸣的精神,多多借鉴国内外先进的测试经验,参考业界流行的行业标准,找到适合自己团队的测试方法和模式,创造更大的社会价值,发挥更大的人生价值。

      实施软件测试自动化的理由分析

      首先,测试人员的工作比以往任何时候都更加困难,因为公司和组织希望以更快的速度和更低的成本开发出高质量的应用程序。
      此外,在很多项目中,测试人员的所有任务实际上都是手动处理的,而实际上,有很大一部分重复性强的测试工作,是可以独立开来自动实现的。
      还有,在大型项目中测试团队和其他的团队之间没有足够的合作,无法促进彼此的工作。
      最后,从个人角度来说,测试人员通常很难花费大量时间来学习新技能;这是目前国内测试从业者的现状,太多的企业为了节约成本而将刚刚走出校门的毕业生作为测试工程师,他们每日做着繁忙的重复工作,又基于自身技能的不深,虽怀博览群书的心愿却不知从何出入手。所谓光阴似箭,因为一转眼我们就说到未来的5年、10年后,我们这些技能不深的测试工程师能做什么呢?而软件测试自动化,也是未来测试工程师或即将成为测试工程一项强有力的工作技能。
      可以说,实施测试自动化是软件行业一个不可逆转的趋势,如果在这个领域走在了前列,无论从企业的核心竞争力还是个人的工作技能来说,都有巨大的优越性,而国内众多的软件厂商也的确在纷至沓来的着手开展着这项工作。

      国内软件测试自动化实施现状分析

      随着众多具有了一定优秀实施自动化测试经验的企业陆续出现,也伴随着很多组织对这项工作依然是丈二的和尚-摸不着头脑。对当前国内软件企业实施或有意向实施测试自动化时面临的主要问题,按实施的不同层次来说:
      ——干脆认为测试自动化是个遥不可及的事情,我们这样的小公司不必实施,人员、资金、资源都不足,以后再说吧!
      ——热血沸腾的实施测试自动化,购买了工具,推行了新的测试流程;几个月后,工具放在那里成了共享资源,测试流程又涛声依旧,回到原来的模式。
      ——公司实施了自动化测试;然而开发与测试之间,甚至与项目经理之间矛盾重重,出了事情不知如何追究责任;虽然还在勉强维持的自动化测试,但实施的成本比手工测试增加了,工作量比从前更大了,从而造成项目团队人员怨声载道,更怀念起那段手工测试的悠闲岁月,唉!那一场风花雪月的事,既然要结束又何必开始…
      ——自动化测试实施相对比较成功,但或多或少还有些问题,比如工具选择不准确,培训不到位,文档不完备,人员分配不合理,脚本可维护度不高等等,造成一种表面上的自动化测试流程,是一幅空架子,如同山间竹笋,嘴尖皮厚腹中空。

      国内软件测试自动化实施不成功原因分析

      ——公司高层意识不到软件测试自动化的重要性;殊不知,其他竞争对手们都大张旗鼓的开展这方面研究和策划的时候,自己还对此持漠视态度,等到整个行业都提高到一个新的层次,那时再着手做,可能就是热锅上的蚂蚁了。
      ——所谓凡事预则立,不预则废。一个软件企业实施测试自动化,绝对不是拍脑袋说干就能干好的,它不仅涉及测试工作本身流程上、组织结构上的调整与改进,甚至也包括需求、设计、开发、维护及配置管理等其他方面的配合。
      ——软件开发是团队工作,在这一领域要尤其注重以人为本;所以人员之间的配合、测试组织结构的设置非常重要,每个角色一定要将自己的责任完全担负起来,这也是减少和解决前述团队矛盾的必要手段。
      ——对开展自动化测试的监督和评估相当重要,也包括对工作产品的检查和人员的考核。一定要将自动化测试全面深入的贯彻到测试工作中,不能敷衍了事,不能做表面工作。这项工作在CMM三级里规范的很好,只可惜我们的很多公司对CMM真的只是“过级”!

      正确认识国内未实施软件测试自动化的根源

      目前国内的软件公司,很多还是处于获取资本的原始积累阶段,我们不能说公司领导完全不重视测试,而是测试整体行业都没有被重视起来,这是其一;其二是公司高层有更需要重视的环节,例如寻找客户签订单,或者开发,这些是直接关系公司存亡的命脉性东西。
      即便企业重视测试,如果公司做一番比较全面的评估(在后续的测试自动化引入入条件里,再详细说明),也不一定非要实施自动化测试。笔者认为一些中小软件公司在大刀阔斧推行自动化测试之前,在测试流程管理、测试缺陷流程、测试人员技能培训等方面做工作,这样可以用比较少的成本投入来获取相对较大且长期的收益回报。
      最后,针对普通测试工程师的一些建议,在这样的公司里,其实有着很多意想不到的优越性:
      (一)这样的公司测试如果不正规,那么你就有更大更多的发挥空间。
      (二)你可以单独学习自动化测试技术,自己先试着应用到日常工作的软件项目中。
      (三)你可以直接和开发人员沟通,甚至向他们学习开发技术,这对将来的自动化测试中开发脚本很有好处。
      (四)每个优秀测试工程师的成长都是个循序渐进的过程。我遇到很多测试界的新手向我发牢骚,很惭愧我没有什么可以告诫他们的,只是希望这样的新人克服浮躁情绪,稳扎稳打练好基本功,想成为优秀的测试专家,就要经历这个阶段。

      软件测试自动化的引入条件

      如果你的测试部门有意向引入自动化测试,那么首先要从思想上统一认识。
      一、 软件测试自动化的正确认识
      1) 自动化测试能大大降低手工测试工作,但决不能完全取代手工测试。完全的自动化测试只是一个理论上的目标,实际上想要达到 100% 的自动化测试,不仅代价相当昂贵,而且操作上也是几乎不可能实现。一般来说,一个 40-60% 的利用自动化的程度已经是非常好的了,达到这个级别以上将过大的增加测试相关的维护成本。
      2) 测试自动化的引入有一定的标准,要经过综合的评估,绝对不能理解成测试工具简单的录制与回放过程。实际上,从实现成熟度来说,自动化测试分五个级别:

    级别 说明 优点 缺点 用法
    一级 录制和回放 自动化的测试脚本能够被自动的生成,而不需要有任何的编程知识 拥有大量的测试脚本,当需求和应用发生变化时相应的测试脚本也必须被重新录制 当测试的系统不会发生变化时,实现小规模的自动化
    二级 录制、编辑和回放 减少脚本的数量和维护的工作 需要一定的编程知识;频繁的变化难于维护 回归测试时,用于被测试的应用有很小的变化
    三级 编程和回放 确定了测试脚本的设计,在项目的早期就可以开始自动化的测试 要求测试人员具有很好的软件技能,包括设计、开发 大规模的测试套件被开发、执行和维护的专业自动化测试
    四级 数据驱动的测试 能够维护和使用良好的并且有效的模拟真实生活中数据的测试数据 软件开发的技能是基础,并且需要访问相关的测试数据 大规模的测试套件被开发、执行和维护的专业自动化测试
    五级 使用动作词的测试自动化 测试用例的设计被从测试工具中分离了出来 需要一个具有工具技能和开发技能的测试团队 专业的测试自动化将技能的使用最优化的结合起来

      3) 自动化测试能提高测试效率,快速定位测试软件各版本中的功能与性能缺陷,但不会创造性的发现测试脚本里没有设计的缺陷。测试工具不是人脑,要求测试设计者将测试中各种分支路径的校验点进行定制;没有定制完整,即便事实上出错的地方,测试工具也不会发觉。因此,制订全面、系统的测试设计工作是相当重要的。
      4) 自动化测试能提高测试效率,但对于周期短、时间紧迫的项目不宜采用自动化测试。推行自动化测试的前期工作相当庞大,将企业级自动化测试框架应用到一个项目中也要评估其合适性,因此决不能盲目的的应用到任何一个测试项目中,尤其不适合周期短的项目,因为很可能需要大量的测试框架的准备和实施而会被拖跨。
      5) 实施测试自动化必须进行多方面的培训,包括测试流程、缺陷管理、人员安排、测试工具使用等。如果测试过程是不合理的,引入自动化测试只会给软件组织或者项目团队带来更大的混乱;如果我们允许组织或者项目团队在没有关于应该如何做的任何知识的情况下实施自动化测试,那将肯定会以失败告终。
      如果软件企业有意向实施自动化测试,那么应该具备什么样的条件才可以引入自动化测试呢,才可以最大可能的减少引入风险,并能够可持续性的开展下去呢?
      二、对企业自身现状的评估分析
      
    第一,从企业规模上来说,没有严格限制。无论公司大小,都需要提高测试效率,希望测试工作标准化,测试流程正规化,测试代码重用化。所以第一要做到的,就是企业从高层CTO开始,直到测试部门的任何一个普通工程师,都要树立实施自动化测试的坚定决心,不能抱着试试看的态度。一般来说,一个这样的软件开发团队可以优先开展自动化测试工作:测试-开发人员比例合适,比如1:1到1:1.5;开发团队总人数不少于10个。当然,如果你的公司只有三五个测试人员,要实施自动化测试绝非易事;不过可以先让一个、两个测试带头人首先试着开展这个工作,不断总结、不断提高,并和层层上司经常汇报工作的开展情况,再最终决定是否全面推行此事。
      第二,从公司的产品特征来说,一般开发产品的公司实施自动化测试要比开发项目的公司要优越些。原因很简单,就是测试维护成本和风险都小。产品软件开发周期长,需求相对稳定,测试人员可以有比较充裕的时间去设计测试方案和开发测试脚本;而项目软件面向单客户,需求难以一次性统一,变更频繁,对开发、维护测试脚本危害很大,出现问题时一般都以开发代码为主,很难照顾到测试代码。但决不是说做项目软件的公司不能实施自动化测试,当前国内做项目的软件公司居多,有很多正在推行CMM等级标准,这是好事情;只要软件的开发流程、测试流程、缺陷管理流程规范了,推行自动化测试自然水到渠成。
      第三,说说标准化的开发和管理流程。不管是CMM还是ISO,不管是开发流程、测试流程还是缺陷管理流程,这里不能一一阐述,可以参考RUPRational Unified Process, Rational 统一过程),可以参考很多业界文献,我只说明一点,也是我们IT从业人员甚至任何从业人员一个很好的工作原则:
      a、 把你想做的写下来(计划管理)
      b、 按照你写下来的去做(行为管理)
      c、 把做的事情记录下来(报告管理)
      d、 出现的问题要设法解决(跟踪管理)
      在测试流程里,这几个要点都一一有所落实;如果你的软件开发团队据此开发软件,那么完全具备实施自动化测试的条件。当然,也许一些公司的测试管理比较混乱,出了问题不知道谁负责,测试人员或开发人员整日碍

    测试阶段 描述 备注
    单元测试/组件测试 这个测试工作通常是开发人员的职责,很多不同的方法能够被使用,比如"测试先行",它是一个测试框架,开发人员在编写代码前编写不同的单元测试,当测试通过时,代码也被完成了。 通过使用正式的单元测试,不仅能够帮助开发人员产出更加稳定的代码,而且能够是软件的整体质量更加的好。
    集成测试 这里的测试工作集中在验证不同的组件之间的集成上。 这种类型的测试通常是被测试系统的更加复杂测试的基础,大量的边缘测试被合并以制造出不同的错误处理测试。
    系统测试 这种测试是通过执行用户场景模拟真实用户使用系统,以证明系统具有被期望的功能。 这里不需要进行自动化的测试。安装测试、安全性测试通常是有手工完成的,因为系统的环境是恒定不变的。
    其它两种非常重要的测试

    回归测试

    回归测试实际上是重复已经存在的测试,通常如果是手工完成的化,这种测试只在项目的结尾执行执行一到两次。 这里完全有潜力应用自动化的测试,你能够在每次构建完成后执行自动化的回归测试,以验证被测试系统的改变是否影响了系统的其他功能。

    性能测试

    性能测试包括以下不同测试形式:
    - 负载测试
    - 压力测试
    - 并发测试
    -.....
    如果没有自动化的测试工具,你将不能执行通过模拟用户的负载实现的高密集度的性能测试。

      其三是软件自动化测试切入方式的风险。正如前面所言,一定要记住将自动化测试与手工测试结合起来使用,不合理的规划会造成工作事倍功半。首先,对于自动化测试率的目标是 10/90 (10% 的自动化测试和 90% 的手工测试)。当这些目标都实现了,可以将自动化测试的使用率提高。对于何种测试情况下引入自动化测试,何时依然采用手工测试,我们分开阐述。
      一般这样的测试条件下使用自动化测试:
      · 项目没有严格的时间压力
      · 具有良好定义的测试策略和测试计划(知道要测试什么 ,知道什么时候测试 )
      · 对于自动化测试你拥有一个能够被识别的测试框架和候选者
      · 能够确保多个测试运行的构建策略
      · 多平台环境需要被测试
      · 拥有运行测试的硬件
      · 拥有关注在自动化过程上的资源
      · 被测试系统是可自动化测试的
    如下条件是宜采用手工测试:
      · 没有标准的测试过程
      · 没有一个测试什么、什么时候测试的清晰的蓝图
      · 在一个项目中,你是一个新人,并且还不是完全的理解方案的功能性和或者设计
      · 你或者整个项目在时间的压力下
      · 在团队中没有资源或者具有自动化测试技能的人· 没有硬件
      其四是企业软件的开发语言风险。当前业界流行的测试工具有几十种,相同功能的测试工具所支持的环境和语言各不相同,这里笔者总结了当前国际上流行的几个软件测试工具生产厂商及一些主要IDE产品,读者可根据参考网址去了解列举工具和更多工具的详细资料。

    生产厂商 工具名称 测试功能简述 网址链接

    Mercury
    Interfactive
    Corporation

    Winruner

    功能测试

    http://www.Mercury.com/
    us/products

    Loadrunner

    性能测试

    QuickTest Pro

    功能测试

    Astra LoadTest

    性能测试

    testdirector

    测试管理

    IBM Rational

    Rational root

    功能测试和性能测试

    http://www.900.ibm.com
    /cn/software/rational/
    us/products

    Rational XDE tester

    功能测试

    Rationaltestmanager

    测试管理

    Rationalpurifyplus

    白盒测试

    Compuware
    Corporation

    QARun

    功能测试

    http://www.compuware.com/products

    QALoad 性能测试
    QADirector 测试管理
    DevPartner Studio Professional

    白盒测试

    Seque software SilkTest 功能测试 http://www.Seque.com/products/index.asp
    Silk 性能测试
    SilkCentral Test
    /Issue Manager
    测试管理
    Empirix e-Tester 功能测试 http://www.Empirix.com/Empirix/
    /Web+Test+Monitoring/Testing+Solutions/
    Integrated+Web+Testing.html
    e-Load 性能测试
    e-Monitor 测试管理
    parasoft Jtest Java白盒测试 http://www.parasoft.com/jsp/
    products.jsp?itemID=12
    C++test C/C++白盒测试
    .NETtest .NET白盒测试
    RadView WebLOAD 性能测试 http://www.radview.com/products
    WebFT 性能测试
    MicroSoft Web Application 
    Stress Tool
    性能测试 http://www.microsoft.com/technet/archive/
    itsolutions/downloads/webtutor.mspx
    Quest Software benchmark Factory 性能测试 http://www.quest.com/benchmark_factory
    Minq Software Pure 功能测试 http://www.minq.com/products/
    Pure 性能测试
    Pure 测试监控
    Seapine Software QA Wizard 功能测试工具 http://www.seapine.com/products
    TestTrack Pro 缺陷管理工具

      其五还要做时间估算。在评估完前面几项指标后,需要估算实施测试自动化的时间周期,以防止浪费不必要的时间,减少在人员、资金、资源投入上的无端消耗。虽然到测试自动化步入正轨以后,会起到事半功倍的效果,但前期的投入巨大,要全面考虑各种因素,明确实施计划并按计划严格执行,才能最大限度降低风险。
      其六是工作流程变更风险。测试团队乃至整个开发组织实施测试自动化,或多或少会因为适应测试工具的工作流程,带来团队的测试流程、开发流程的相应变更,而且,如果变更不善,会引起团队成员的诸多抱怨情绪;所以应该尽量减少这种变更,并克服变更中可能存在的困难。
      其七是人员培训与变更风险。简单而言,就是测试团队人员的培训具有风险性,例如每个角色的定位是否准确,各角色人员对培训技能的掌握程度是否满意,尤其实施途中如果发生人员变更等风险,都要事先做出预测和相应的处理方案。
      一个企业或软件团队实施测试自动化,会有来自方方面面的压力和风险,但是凭借团队成员的聪明才智和公司高层的大力支持,事先做好评估,做好风险预测,那么可以告诉你一个激动人心的消息:你的团队成功引入了测试自动化!有了测试自动化,我们即可享受它带来的超凡价值和无穷魅力:我们的测试工作变得更简单、更有效,我们工作在一个专家级的团队里,因此我们每天都在享受这种成功的喜悦!

  • QTP连接MySQL操作

    2009-09-27 15:38:06

    一直想研究如何使用QTP连接MySQL数据库的问题,因为MySQL属于开源DB,所以其ODBC驱动也自然不会在系统中预先为你存在.所以想使用MySQL的话,还得事先费点周折.

    Windows ODBC数据源的管理:

    本人的是Windows7 64-bit系统, ODBC数据源的访问路径与Vista基本一致.

    具体路径为:Control Panel > System and Security > Administrative Tools > Data Source(ODBC)

    打开ODBC后,添加MySQL ODBC驱动即可. MySQL ODBC驱动到网上可以很方便的下载安装,安装成功后会自动添加到ODBC数据源的Driver页下.

    具体如何下载安装以及添加MySQL ODBC数据源,不作赘述.

    下面提供一个具体的QTP连接MySQL的例子:

    本例中以数据"test"以及其中的"bed"表为例,进行操作演示:

    Dim cnn, strCnn, Rst

    strCnn = "DRIVER={MySQL ODBC 5.1 Driver};SERVER=localhost;DATABASE=test;USER=root;PASSWORD=123;Option=3"
    Set cnn = CreateObject("ADODB.connection")
    cnn.Open strCnn
    Set Rst = CreateObject("ADODB.Recordset")
    Rst.Open "select * from bed", Cnn
    Rst.MoveFirst
    While Rst.EOF <> true
     print Rst("ID")& "   " & Rst("EnrollID")&  "   " & Rst("DromID")& chr(13) & chr(10)
     Rst.MoveNext
    Wend

    Rst.Close
    Cnn.Close

    Set Rst = nothing
    Set Cnn = nothing

    本例很简单,主要操作就是连接MySQL数据库,同时查询"test"库中的"bed"表三个字段的值并打印出来.

    值得注意的是,操作完毕后必须关闭数据库连接,并置空相关的变量,这是一个好的编程习惯.

  • Unix 设置时区时间的命令

    2009-08-18 13:56:16

    不短时间没来更新点东西了,刚好用到了关于设置Unix环境下设置时区时间的命令,写上来等记不得的时候可以过来翻阅.

    开始吧:

    设置时区命令:tzselect

    回车执行该命令,出现世界上各个大洲的列表,输入大洲前序号,然后回车即可选择该大洲.

    大洲选择完毕后,自动出现该大洲上的所有国家列表,同样输入相应序号更改国家信息.

    最后跳出时区选择信息,选择相应时区即可.

    最后可以使用:date命令查看Unix系统的本地时间.

     

    修改Unix系统本地时间,使用如下格式命令:

    date -s "2009/08/17 13:34:00"

    通过如上两个命令即可修改Unix系统的时区和时间.

     

  • Kobe Bryant FMVP

    2009-06-17 17:58:04

    七年了,七年的期待,七年的拼搏,你做到了,祝福你,替你开心。

    期待你带给我另外的惊喜....默默关注中.....

431/3123>
Open Toolbar