海是我向往的地方,吸纳和咆哮是他的魅力!!!

发布新日志

  • 卡性质总结

    zhaojing414 发布于 2010-12-22 14:53:26

    1、借记卡
         先存钱后使用,消费的金额不能大于卡内金额,不能透支,计存款利息。
    2、贷记卡
         狭义上的信用卡,先用后还,需要一次性付清欠款,但要支付一定的利息,取款利息较高。
    3、准贷记卡
          兼具借记卡和贷记卡的功能,先存后用,可透支一定金额,计存款利息,欠款必须一次性付清。目前我的工资卡为准贷记卡,消费的金额在账户中作为负资产,到结算时将消费金额从卡内扣除,也是说能预先透支,但透支有上限,这要根据个人信誉度来授信。
    4、预付费卡
          就是先充值后使用,消费的金额不足则不能进行消费。
     
  • TCP与UDP的区别

    zhaojing414 发布于 2011-03-08 16:33:55

    我们学习过什么是“数据包”。理解数据包,对于网络管理的网络安全具有至关重要的意义。比如,防火墙的作用本质就是检测网络中的数据包,判断其是否违反了预先设置的规则,如果违反就加以阻止。图1就是瑞星个人版防火墙软件设置规则的界面。细心的读者会发现,图1中的“协议”栏中有“TCP”、“UDP”等名词,它们是什么意思呢?现在我们就来讲讲什么是TCP和UDP。

    面向连接的TCP

    “面向连接”就是在正式通信前必须要与对方建立起连接。比如你给别人打电话,必须等线路接通了、对方拿起话筒才能相互通话。

    图1

    TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来,其中的过程非常复杂,我们这里只做简单、形象的介绍,你只要做到能够理解这个过程即可。我们来看看这三次对话的简单过程:主机A向主机B发出连接请求数据包:“我想给你发数据,可以吗?”,这是第一次对话;主机B向主机A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包:“可以,你什么时候发?”,这是第二次对话;主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”,这是第三次对话。三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机A才向主机B正式发送数据。

    TCP协议能为应用程序提供可靠的通信连接,使一台计算机发出的字节流无差错地发往网络上的其他计算机,对可靠性要求高的数据通信系统往往使用TCP协议传输数据。

    图2

    我们来做一个实验,用计算机A(安装Windows 2000 Server操作系统)从“网上邻居”上的一台计算机B拷贝大小为8,644,608字节的文件,通过状态栏右下角网卡的发送和接收指标就会发现:虽然是数据流是由计算机B流向计算机A,但是计算机A仍发送了3,456个数据包,如图2所示。这些数据包是怎样产生的呢?因为文件传输时使用了TCP/IP协议,更确切地说是使用了面向连接的TCP协议,计算机A接收数据包的时候,要向计算机B回发数据包,所以也产生了一些通信量。

    图3

    如果事先用网络监视器监视网络流量,就会发现由此产生的数据流量是9,478,819字节,比文件大小多出10.96%(如图3所示),原因不仅在于数据包和帧本身占用了一些空间,而且也在于TCP协议面向连接的特性导致了一些额外的通信量的产生。

    面向非连接的UDP协议

    “面向非连接”就是在正式通信前不必与对方先建立连接,不管对方状态就直接发送。这与现在风行的手机短信非常相似:你在发短信的时候,只需要输入对方手机号就OK了。

    UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去!

    图4

    UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境。比如,我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常,其实“ping”命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。例如,在默认状态下,一次“ping”操作发送4个数据包(如图2所示)。大家可以看到,发送的数据包数量是4包,收到的也是4包(因为对方主机收到后会发回一个确认收到的数据包)。这充分说明了UDP协议是面向非连接的协议,没有建立连接的过程。正因为UDP协议没有连接的过程,所以它的通信效果高;但也正因为如此,它的可靠性不如TCP协议高。QQ就使用UDP发消息,因此有时会出现收不到消息的情况。

      附表:tcp协议和udp协议的差别

    TCP协议和UDP协议各有所长、各有所短,适用于不同要求的通信环境。TCP协议和UDP协议之间的差别如附表所示。

  • 性能测试报告模板

    cow 发布于 2011-03-08 22:55:23

    1、测试项目概述与测试目的

    1.1 项目概述

    本部分主要是针对即将进行压力测试的对象(接口、模块、进程或系统)进行概要的说明,让人明白该测试对象的主要功能与作用及相关背景。

    1.2 测试目标(目的)

        简要列出进行本次压力测试的主要目标(目的)

    1.3 名词解释

        性能测试过程中涉及的业务和技术方面的专业名词

    1.4 参考文档

    列出与本文档相关的参考文档名称

    2、测试对象的拓扑结构

    本部分主要以图表加文字的方式,对待测试对象(接口、模块、系统)的拓扑结构进行描述,并标上必要的数据流向。注意:若生产实际跨越物理主机的模块(进程,数据库)部署应在拓扑图中要标示出来。

    3、测试环境与测试数据

    3.1 测试环境

    主要指软件实际运行的平台,以及软硬件配置,操作系统及版本,数据库名称及版本,客户端机器配置等方面内容

    3.2 测试数据

         根据性能(压力)测试方案(计划)中测试数据的要求,结合测试方案与测试用例,构造符合要求的测试数据(包括系统初始数据与测试发送数据),并描述测试数据的总量及简述这些测试数据生成的方法。

    4  测试策略

    4.1 测试方案

        根据测试目的,写出测试的总体方案(方法)及所采用的技术手段等。

    4.2 测试场景

        针对测试目的,结合所测对象的具体特征,设计出达到要求的并且符合真实生产场景的测试场景。

    4.3测试用例

        根据测试场景,转换成对应的测试用例。

    5、测试执行步骤

        具体描述每个场景的测试执行步骤,并同时说明采集的相关指标值。

    6  测试结果

        针对每一个测试场景的相关测试观测指标要进行采集与记录(测试执行前,过程中,执行完),指标的采集可以通过工具,手工以及编写脚本相结合的方法获得,并把采集的这些指标值通过表格或图表的方式陈列出来。

    7  测试结果分析

    根据收集的测试结果,首先要进行程序资源消耗分析(cpu,内存,磁盘)IO分析,接着要根据测试目的(目标)项进行对应分析,最后根据测试结果记录表中各个场景的对比分析,从中分析归纳出影响系统压力性能的关键影响因素(可选),并借助图表的方式来表达。直观且有说服力。

    8  程序改进与建议

        如果测试结果与测试目标值相差太远或达不到,结合测试过程中所观测到的各种信息,测试人员有针对性提出程序的改进方向与建议(包括系统参数或配置文件的配置),供开发人员改进参考或生产程序部署运行配置参考。

    9  测试结论

    根据测试结果与测试分析,得出性能(压力)测试是否通过的结论。只有2种结论,通过或者不通过。 同时要增加因测试环境与真实环境差异、测试数据模型与真实数据模型差异以及测试场景与真实场景差异的大小评估对测试结果或结论的影响。

  • 性能测试方案(计划)模板

    cow 发布于 2011-03-08 22:48:43

     

    1、    概述

    1.1  目的

    本部分主要是针对即将进行性能测试的对象(接口、模块、进程或系统)进行概要的说明,让人明白该测试对象的主要功能与作用及相关业务背景。同时要指出进行性能测试的驱动原因。

    1.2   名词解释

    此方案中涉及的业务和技术方面的专业名词。

    1.3  参考资料

    此方案参考和依据的所有文档

    2、    测试对象的拓扑结构

    本部分主要以图表加文字的方式,对待测试对象(接口、模块、系统)的拓扑结构进行描述,并标上必要的数据流向。注意:若生产实际跨越物理主机的模块(进程,数据库)部署应在拓扑图中要标示出来。

    3、    测试目的与测试范围

    根据测试的驱动原因,结合待测对象的生产实际,进行性能测试需求分析,抽取本次性能测试需要达到的各种目标,即性能指标(主要是业务目标能否满足要求,系统资源消耗是否是可接受的范围内)。

    根据测试的目的,待测对象与业务数据流很容易明确测试的范围。

    4、    测试策略

    软件测试策略:在一定的软件测试标准、测试规范的指导下,依据测试项目的特定环境约束而规定的软件测试的原则、方式、方法的集合。

    4.1  性能测试准入条件、通过标准,失败与挂起标准

    用于明确描述性能测试准入的条件、通过、挂起与失败标准

    4.2  测试方法与所需求的技术要求

    用于描述完成性能测试采取的方法、手段及步骤,同时,指出要完成测试所必备的技术能力,如编写模拟器、脚本以及性能数据收集与分析的能力等等。

    4.3  测试所需要的工具选择

    有时做性能测试,可能要借助于某种自动化的工具或通过自身的开发来达到,因此,要结合项目实际与工具的性价表,进行合理的选择。

    5、    测试环境

    用于描述基于软件的拓扑图,描述待测对象运行的操作系统与版本,选用中间件的名称与版本以及所需的数据库名称及版本。同时应标明各主机未进行压力测试时的平均负载

    6、    测试数据与测试场景

    6.1  测试数据准备

         主要用于描述基础数据与测试执行时用到的测试数据准备。测试数据准备的模型以及测试场景的选择,越接近系统真实的场景,对测试结论的准确性至关重要。因此,要描述数据准备的依据。   (初始数据:系统运行所必需的初始化数据;业务数据:提供负载压力背景;脚本中参数数据:参数数据要真实模拟负载)

    6.2  测试场景的选取

    针对测试目的,结合所测对象的具体特征,设计出达到要求的并且符合真实生产场景的测试场景。测试场景选择的基本依据主要为:关键核心业务流程(高吞吐量),高数据库io高商业风险的业务流程

    7、    测试所需资源

    所需资源主要包括如下三个方面

    硬件资源: 主要描述要完成待测对象的性能测试,结合拓扑结构图,列出必需的机器资源(服务器、小型机、客户机)

    软件资源:硬件资源中用到的操作系统、数据库以及中件间以及应用服务器等必需软件

    人力资源:用于描述为完成性能测试所需要的各种人力资源(用例编写、测试数据生成、脚本编写、及性能结果与分析、性能调优)

    8、    工作量预估与测试进度

    根据测试策略与相关任务,进行各项任务的人员、时间、计划安排,让进度可控

    9、    测试风险

        测试风险主要是指一切可能会影响性能测试执行的进度或准确性的因素进行描述,并且评估一下影响的程度。若影响很小,可不用进行说明。下面列举了可能会遇到的一些风险。

    A 管理风险

    进度安排风险

    软件版本控制风险
    人力资源风险(人员不到位或工作时间难保证等)

    B 技术风险

    测试工具风险
    测试开发的技术风险

    测试环境风险(无机器资源与相应的软件环境)

    C 协作风险

    测试环境稳定性风险(性能测试期间,有其它人员同时在测试环境上进行测试)

    上下游模块或系统配合测试风险

    D 测试结果准确性风险

    测试环境与真实环境差异以及测试数据模型与真实数据模型差异可能导致结果不准确性风险

    10、   测试完成交付物

    描述整个性能测试过种中,相应的输出文档(性能测试需求分析、性能测试方案(计划)、性能测试结果与报告)

  • 软件基础知识

    小鱼儿乖乖 发布于 2011-03-08 22:24:35

    1、什么是软件测试?

    软件测试就是利用测试工具按照测试方案和流程对产品进行功能和性能测试,甚至根据需要编写不同的测试工具,设计和维护测试系统,对测试方案可能出现的问题进行分析和评估。执行测试用例后,需要跟踪故障,以确保开发的产品适合需求。

    2、软件测试的目的是什么?

    软件测试是程序的一种执行过程,目的是尽可能发现并改正被测试软件中的错误,提高软件的可靠性。

    3、软件测试的分类

    从是否关心软件内部结构和具体实现的角度划分

      A.白盒测试

      B.黑盒测试

      C.灰盒测试
    从是否执行程序的角度

      A.静态测试

      B.动态测试。
    从软件开发的过程按阶段划分有

      A.单元测试

      B.集成测试

      C.确认测试

      D.系统测试

      E.验收测试

    4、什么是黑盒测试?白盒测试?灰盒测试?

    黑盒测试(Black-box Testing,又称为功能测试或数据驱动测试)是把测试对象看作一个黑盒子。利用黑盒测试法进行动态测试时,需要测试软件产品的功能,不需测试软件产品的内部结构和处理过程。

    白盒测试也称结构测试或逻辑驱动测试,它是按照程序内部的结构测试程序,通过测试来检测产品内部动作是否按照设计规格说明书的规定正常进行,检验程序中的每条通路是否都能按预定要求正确工作。 这一方法是把测试对象看作一个打开的盒子,测试人员依据程序内部逻辑结构相关信息,设计或选择测试用例,对程序所有逻辑路径进行测试,通过在不同点检查程序的状态,确定实际的状态是否与预期的状态一致。

    灰盒测试,是介于白盒测试与黑盒测试之间的,可以这样理解,灰盒测试关注输出对于输入的正确性,同时也关注内部表现,但这种关注不象白盒那样详细、完整,只是通过一些表征性的现象、事件、标志来判断内部的运行状态,有时候输出是正确的,但内部其实已经错误了,这种情况非常多,如果每次都通过白盒测试来操作,效率会很低,因此需要采取这样的一种灰盒的方法。


    5、黑盒测试用例的设计方法有那些?

    等价类划分方法·边界值分析方法·错误推测方法·因果图方法·判定表驱动分析方法·正交实验设计方法·功能图分析方法

    6、什么是软件质量?

    概括地说,软件质量就是“软件与明确的和隐含的定义的需求相一致的程度”。具体地说,软件质量是软件符合明确叙述的功能和性能需求、文档中明确描述的开发标准、以及所有专业开发的软件都应具有的隐含特征的程度。

    7、软件验收测试的合格通过准则是?

    1)、软件需求分析说明书中定义的所有功能已全部实现,性能指标全部达到要求。  

    2)、所有测试项没有残余的一级二级三级的错误。  

    3)、立项审批表、需求分析文档、设计文档和编码实现一致。

    4)、验收测试工件齐全(测试计划,测试用例,测试日志,测试通知单,测试分析报告)

    8、性能测试(或称多用户并发性能测试)、负载测试、强度测试、容量测试是性能测试领域里的几个方面
    性能测试(Performance Test):通常收集所有和测试有关的所有性能,通常被不同人在不同场合下进行使用。测试软件在系统中的运行性能,度量系统与预定义目标的差距。
    关注点:how much和how fast

    负载测试(Load Test):负载测试是一种性能测试,指数据在超负荷环境中运行,程序是否能够承担。通过逐步增加系统负载,确定在满足性能指标的情况下,系统所能承受的最大负载量。
    关注点:how much

    强度测试(Stress Test):强度测试是一种性能测试,他在系统资源特别低的情况下软件系统运行情况,目的是找到系统在哪里失效以及如何失效的地方。包括
    Spike testing:短时间的极端负载测试
    Extreme testing:在过量用户下的负载测试
    Hammer testing:连续执行所有能做的操作
    压力测试:通过逐步增加系统负载,确定在什么负载条件下系统处于失效状态,以此来获得系统能提供的最大服务级别。

    容量测试(Volume Test):确定系统可处理同时在线的最大用户数,使系统承受超额的数据容量来发现它是否能够正确处理。

    关注点:how much(而不是how fast)
    容量测试,通常和数据库有关,容量和负载的区别在于:容量关注的是大容量,而不需要表现实际的使用。

    其中,容量测试、负载测试、强度测试的英文解释为:
    Volume Testing = Large amounts of data
    Load Testing = Large amount of users
    Stress Testing = Too many users, too much data, too little time and too little room


    举例:一个人背X斤
    负载测试:200斤情况下,是否能坚持5分钟。
    压力测试:200,300,400...斤情况下,他的表现,什么时候失败,失败之后什么表现,重新扛200是否正常。
    容量测试:在坚持5分钟的情况下,他一次最多能扛多少斤。


  • qtp连接oracle

    liuxueyi 发布于 2010-11-25 00:09:17

     

    《第一步》添加数据源。

    1.选择添加

     

    2.找到要添加的数据库应用软件,然后“完成”。

    3.请参照我的连接 ,选择“Test Connection

     

    4.输入密码

     

    5.测试连接数据源成功。

     

    6.此时添加成功。

     

    《第二阶段》获取连接路径(这是最经典的一步,我在网上搜了很久都没有搜到,还是请教的专家)。

     

    1.新建一个文本文件

    2.更改后缀,为”udl”

    3.双击打开,连接测试。

    4.测试成功。

    5.以文本方式打开,获取路径。如:Provider=MSDASQL.1;Password=orcl;Persist Security Info=True;User ID=system;Data Source=ORCL

    6.以后的都不用说了吧。

     

  • Linux性能监控之Memory篇(2)

    luxuabc 发布于 2007-11-18 20:32:42

    我们来看内存监控的一个例子,用vmstat命令的输出如下:

    # vmstat 3
    procs          memory            swap     io        system      cpu
    r b  swpd   free   buff cache   si so    bi   bo    in   cs   us sy id wa
    3 2 809192 261556 79760 886880 416 0    8244  751   426  863  17 3   6 75
    0 3 809188 194916 79820 952900 307 0    21745 1005  1189 2590 34 6  12 48
    0 3 809188 162212 79840 988920 95  0    12107 0     1801 2633 2  2   3 94
    1 3 809268 88756 79924 1061424 260 28   18377 113   1142 1694 3  5   3 88
    1 2 826284 17608 71240 1144180 100 6140 25839 16380 1528 1179 19 9  12 61
    2 1 854780 17688 34140 1208980 1   9535 25557 30967 1764 2238 43 13 16 28
    0 8 867528 17588 32332 1226392 31  4384 16524 27808 1490 1634 41 10  7 43
    4 2 877372 17596 32372 1227532 213 3281 10912 3337  678  932  33 7   3 57
    1 2 885980 17800 32408 1239160 204 2892 12347 12681 1033 982  40 12  2 46
    5 2 900472 17980 32440 1253884 24  4851 17521 4856  934  1730 48 12 13 26
    1 1 904404 17620 32492 1258928 15  1316 7647  15804 919  978  49 9  17 25
    4 1 911192 17944 32540 1266724 37  2263 12907 3547  834  1421 47 14 20 20
    1 1 919292 17876 31824 1275832 1   2745 16327 2747  617  1421 52 11 23 14
    5 0 925216 17812 25008 1289320 12  1975 12760 3181  772  1254 50 10 21 19
    0 5 932860 17736 21760 1300280 8   2556 15469 3873  825  1258 49 13 24 15

    其中:swpd为虚拟内存的使用大小单位为KB.

         Free为空闲的物理内存的大小(KB);

         Buff为内存中缓存的大小,这些缓存是read()和write()函数使用的(KB);

         Cache进程的地址空间在物理内存中的映射(KB);

         So为从内存写入swap空间的数据大小(KB);

         Si为从swap空间写入内存的数据大小(KB);

         Bo为从内存写入硬盘或swap的页数量;

         Bi为从硬盘或swap写入内存的页数量;

    从上面的输出我们可以看到:

    1. 大量的disk pages(bi)被写入内存,这点可以从cache的不断增长来证明;

    2. 在这个过程中,物理内存始终保持在17MB虽然不断有数据从硬盘读入来消耗内存;

    3. 为了保持可用物理内存,kswapd不断的从Buff中偷取内存,来加入空闲列表,buff不断减小;

    4. 同时kswapd不断的将垃圾页写入swap空间,我们可以看到so和swpd不断增加.

    现在我们可以得出结论,这是一个IO Bound的程序,并且造成了虚拟内存的大量使用,加大物理内存可以改善性能.

    总结下来:

    1. 当一个系统有越少的页错误(所需数据不在内存,需要从硬盘读入),就会有越好的响应时间.因为内存比硬盘快得多.

    2. Free memory数量低是一个好的征兆,因为证明了cache在起作用,除非同时存在大量的bi和so.

    3. 如果一个系统有持续的si和so,就说明系统的内存是一个瓶颈.

  • 软件测试面试题(软通动力,博彦科技,奇虎,瑞星,中软)

    jxxshenchen 发布于 2009-11-25 17:33:45

    过去了就过去了

    失败者跌倒的时候往后看,成功者跌倒的时候还是拼命朝前看

    我要做成功者。。

    转来面试题,有时间多看看,多积累经验。

    1。软通动力面试笔答
      
    1.白箱测试和黑箱测试是什么?什么是回归测试?

    2.单元测试、集成测试、系统测试的侧重点是什么?
    单元测试的重点是系统的模块,包括子程序的正确性验证等。
    集成测试的重点是模块间的衔接以及参数的传递等。
    系统测试的重点是整个系统的运行以及与其他软件的兼容性。

    3.设计用例的方法、依据有那些?
       白盒测试用例设计有如下方法:基本路径测试\等价类划分\边界值分析\覆盖测试\循环测试\数据流测试\程序插桩测试\变异测试.这时候依据就是详细设计说明书及其代码结构吧;
    黑盒测试用例设计方法:基于用户需求的测试\功能图分析方法\等价类划分方法\边界值分析方法\错误推测方法\因果图方法\判定表驱动分析方法\正交实验设计方法.依据是用户需求规格说明书,详细设计说明书

    4.一个测试工程师应具备那些素质和技能?
    掌握基本的测试基础理论
    本着找出软件存在的问题的态度进行测试,即客观吧,不要以挑刺形象出现
    可熟练阅读需求规格说明书等文档
    以用户的观点看待问题
    有着强烈的质量意识
    细心和责任心
    良好的有效的沟通方式(与开发人员及客户)
    具有以往的测试经验
    能够及时准确地判断出高危险区在何处.

    5.集成测试通常都有那些策略?
    大爆炸集成;自顶向下集成;自底向上集成;三明治集成;分层集成;基干集成;基于功能的集成;基于消息的集成;基于风险的集成;基于进度的集成.
    6.你用过的测试工具的主要功能、性能及其他?

    7.一个缺陷测试报告的组成?
    缺陷跟踪报告:
    编号
    如:ut-dt00016
    标题
    如:文字排版功能.字间距.MarchCalculator计算错误
    版本号
    如:V1.3
    执行状态
    如:空白/草稿/提交/审批/分发/正在修改/修改完毕/正在确认/关闭…
    修改记录
    如:2003年7月2日;肖睿编制/修改;原因
    测试环境和版本号码、程序编写人员
    错误严重程度和优先级别
    错误详细描述
    重现步骤和方式、对应的测试记录编码
    附件
    建议修改方式
    修改内容、结果及修改人员签字/日期

    8.基于WEB信息管理系统测试时应考虑的因素有哪些?
    一、功能测试
        1、链接测试
        2、表单测试
        3、Cookies测试
        4、设计语言测试
        5、数据库测试
    二、性能测试
        1、连接速度测试
        2、负载测试
        3、压力测试
    三、可用性测试
        1、导航测试
        2、图形测试
        3、内容测试
        4、整体界面测试
    四、客户端兼容性测试
        1、平台测试  
        2、浏览器测试
    五、安全性测试

    9.软件本地化测试比功能测试都有哪些方面需要注意?

    10.软件测试项目从什么时候开始,?为什么?
    软件测试应该在需求分析阶段就介入,因为测试的对象不仅仅是程序编码,应该对软件开发过程中产生的所有产品都测试,并且软件缺陷存在放大趋势.缺陷发现的越晚,修复它所花费的成本就越大.

    11.需求测试注意事项有哪些?
    一个良好的需求应当具有以下特点:

    完整性:每一项需求都必须将所要实现的功能描述清楚,以使开发人员获得设计和实现这些功能所需的所有必要信息。
    正确性:每一项需求都必须准确地陈述其要开发的功能。
    一致性:一致性是指与其它软件需求或高层(系统,业务)需求不相矛盾。
    可行性:每一项需求都必须是在已知系统和环境的权能和限制范围内可以实施的。
    无二义性:对所有需求说明的读者都只能有一个明确统一的解释,由于自然语言极易导致二义性,所以尽量把每项需求用简洁明了的用户性的语言表达出来。
    健壮性:需求的说明中是否对可能出现的异常进行了分析,并且对这些异常进行了容错处理。
    必要性:“必要性”可以理解为每项需求都是用来授权你编写文档的“根源”。要使每项需求都能回溯至某项客户的输入,如Use Case或别的来源。
    可测试性:每项需求都能通过设计测试用例或其它的验证方法来进行测试。
    可修改性:每项需求只应在S R S 中出现一次。这样更改时易于保持一致性。另外,使用目录表、索引和相互参照列表方法将使软件需求规格说明书更容易修改。
    可跟踪性:应能在每项软件需求与它的根源和设计元素、源代码、测试用例之间建立起链接链,这种可跟踪性要求每项需求以一种结构化的,粒度好(f i n e - g r a i n e d )的方式编写并单独标明,而不是大段大段的叙述。



    12.简述一下缺陷的生命周期

    13.分析测试用例注意(事项)?
    1.为什么要写用例:
    我们编写测试用例,有如下的好处:
    便于团队交流:假如说一个测试团队有10个成员,大家测试的时候都各自为政,没有统一的标准,测试的效率无疑会大打折扣;如果大家都遵循统一的用例规范去写,就会解决这一问题。
    便于重复测试 :大家知道,软件在实际开发过程中是会有不同版本的,比如会从1.0升级到10.0,那么如果不写测试用例的话,在测试10.0版本的时候,你能完全记得1.0版本时你做过哪些测试吗?测试用例就像一个备忘录一样,便于重复测试。
    便于跟踪统计:这一点是针对测试经理或是项目经理来说的,项目负责人通过看测试用例的执行情况,就能了解到项目目前的概况,比如已经执行了哪些测试,还有哪些测试没有执行,测试没有通过的地方主要集中在哪些模块等。
    便于用户自测:尤其是项目软件,有的时候用户希望自己测试一下软件产品,但是用户大都是非专业人士,他需要根据你写好的用例来更好的检验产品的质量
    说了这么多编写测试用例的优点,那它有没有缺点呢?有一个明显的缺点就是需要花费大量的时间,通常编写测试用例的时间比实际执行测试的时间还要长,这一点大家会在实际工作中有深刻的体会

    2.什么时候写用例:
    什么时候写用例?这个问题没有统一的标准答案,但有一点可以肯定,就是测试用例要尽早编写。 大家认为在哪个阶段开始写用例比较好呢?
    通常,我们都会在测试设计阶段来写用例,即《需求规格说明书》和《测试计划》都已完成之后



    二。瑞星笔试题(15道)
      
    1.一台计算机的IP是192.168.10.71子网掩码255.255.255.64与192.168.10.201是同一局域网吗?
    2.internet中e-mail协仪,IE的协仪,NAT是什么,有什么好处,能带来什么问题?DNS是什么,它是如何工作的?
    3.PROXY是如何工作的?
    4.win2k系统内AT命令完成什么功能,Messenger服务是做什么,怎么使用?
    5进程,线程的定义及区别
    6,32位操作系统内,1进程地址空间多大,进程空间与物理内存有什么关系?
    7网络攻击常用的手段,防火墙如何保证安全.
    8如何配静态IP,如何测网络内2台计算机通不通,PING一次返几个数据包?
    9WIN9X与WINNT以上操作系统有"服务"吗,服务是什么,如何停止服务?
    10AD在WIN2KSERVER上建需什么文件格式,AD是什么?XP多用户下"注销"与"切换"的区别.
    11UDP可以跨网段发送吗?
    12最简单的确认远程计算机(win2K以上)某个监听端口是正常建立的?
    13软件测试的定义,测试工作是枯燥反复的,你是如何理解的?黑盒,白盒,回归,压力测试的定义.
    14winrunner,loadrunner是什么,区别
    15磁盘分区如何分类,请举例说明安装操作系统的注意事项.
    (1小时答题)


    三。中软的面试题

      
    一.     简答题.
    1.     避免死锁的方法有哪些?
    2.     在Sybase数据库中注册用户与数据库用户有什么区别?
    3.     在MS SQL_Server 数据库中通过什么约束保证数据库的实体完整性
    4.     内存有哪几种存储组织结构.请分别加以说明
    5.     JAVA中的Wait() 和notify()方法使用时应注意些什么?
    6.     用户输入一个整数.系统判断,并输出是负数还是非负数,请设计测试用例.
    7.     操作系统中的同步和互诉解决了什么问题
    8.     UNIX 中init
    二.     编写类String 的构造函数,析构函数和赋值函数
    已知类String 的原型为
    class string
    {
    public:
    string(const char *str=null);//普通构造函数
    string(const string &other);//拷贝构造函数
    ---string(void);
    string &operate=(const string &other);//赋值函数
    private:
    char * m-data;//用于保存字符串
    };
    请编写string 的上述4个函数
    三.     有关内存的思考题
    1.     void getmemory(char *p)
    { p=(char*)mallol(100);
    }
    void test(void)
    {
    char * str =null;
    getmemory(str);
    strcpy(str,”hello,world”);
    printf(str);
    }
    请问运行Test函数会有什么样的结果
    2.     char*getmemory(void)
    { char p[]=”hello world”;
    return p;
    }
    void test(void)
    {
    char *str=null;
    str=Getmemory();
    printf(str);
    } 请问运行Test 函数会有什么样的结果.

    三。奇虎面试题

      

    前三道程序题
    (下面的题不排序,有笔试题,也有面试题)
    4、怎么划分缺陷的等级?
    5、怎么评价软件工程师?
    6、软件工程师的素质是什么?
    7、怎么看待软件测试?
    8、软件测试是一个什么样的行业?
    9、图书(图书号,图书名,作者编号,出版社,出版日期)
       作者(作者姓名,作者编号,年龄,性别)
       用SQL语句查询年龄小于平均年龄的作者姓名、图书名,出版社。
    10、你的职业生涯规划
    11、测一个三角形是普通三角形、等腰三角形、等边三角形的流程图,测试用例。
    12、写出你常用的测试工具。
    13、lordrunner分哪三部分?
    14、希望以后的软件测试是怎么样的一个行业?
    15、.软件测试项目从什么时候开始?
       我答:从软件项目的需要分析开始。
         问:为什么从需求分析开始?有什么作用?

    四。北京博彦科技笔试+面试



    笔试题

    1.文件格式系统有哪几种类型?分别说说win95、win98、winMe、w2k、winNT、winXP分别支持那些文件系统。
    2.分别填入一个语句,完成下面的函数,通过递归计算数组a[100]的前n个数之和。
    Int sum ( int a[],int n )
    {
       if (n>0) return___________________________;
       else return________________________;
    }

    3.写出你所知道的3种常用的排序方法,并用其中一种方法设计出程序为数组a[100]排序。
    4.什么是兼容性测试?兼容性测试侧重哪些方面,请按照优先级用矩阵图表列出。
       (这题的第二问我不会答,所以原题目记得不是很清楚,大家能看明白问什么就好)
    5.我现在有个程序,发现在WIN98上运行得很慢,怎么判别是程序存在问题还是软硬件系统存在问题?
    6.翻译,中——英,有关P2P点对点文件传输的原理。
    7.翻译,英——中,有关互联网的发展对商务、学习、交流的影响。

    笔试完了是初步的面试
    先问了个问题:FAT16/FAT32/NTFS 哪个的安全性最好,为什么?(不会答)
    又做了两道题,
    一题是关于C++类的继承,看程序写出输出结果,A是虚类,B继承A,跟一般C++的书上的习题差不多。
    一题是写出在32位机器下,计算几个变量的size,

  • 性能测试之linux指令篇1

    thefirstred 发布于 2007-11-28 11:08:56

    Top命令

    说明:

    显示系统当前的进程和其他状况; top是一个动态显示过程,即可以通过用户按键来不断刷新当前状态.如果在前台执行该命令,它将独占前台,直到用户终止该程序为止. 比较准确的说,top命令提供了实时的对系统处理器的状态监视.它将显示系统中CPU最“敏感”的任务列表.该命令可以按CPU使用.内存使用和执行时间对任务进行排序;而且该命令的很多特性都可以通过交互式命令或者在个人定制文件中进行设定。

    使用格式:

    top [-] [d delay] [q] [c] [S] [s] [i] [n] [b]

    d : 改变显示的更新速度,单位为秒,或是在交谈式指令列( interactive command) s

    q : 没有任何延迟的显示速度,如果使用者是有 superuser 的权限,则 top 将会以最高的优先序执行

    c : 切换显示模式,共有两种模式,一是只显示执行档的名称,另一种是显示完整的路径与名称

    S : 累积模式,会将己完成或消失的子行程 ( dead child process ) CPU time 累积起来

    s : 安全模式,将交谈式指令取消, 避免潜在的危机

    i : 不显示任何闲置 (idle) 或无用 (zombie) 的行程

    n : 更新的次数,完成后将会退出 top

    b : 批次档模式,搭配 "n" 参数一起使用,可以用来将 top 的结果输出到档案内

    结果说明:

    top命令显示的项目很多,默认值是每5秒更新一次,当然这是可以设置的。显示的各项目为:

      第一行为uptime,显示系统启动时间、已经运行的时间、登录用户个数和三个平均负载值(最近1分钟,5分钟15分钟的负载值)。
      第二行显示自最近一次刷新以来的运行进程总数。当然这些进程被分为正在运行的,休眠的,停止的等很多种类。进程和状态显示可以通过交互命令t来实现。
      CPU states 显示用户模式,系统模式,优先级进程(只有优先级为负的列入考虑)和闲置等各种情况所占用CPU时间的百分比。优先级进程所消耗的时间也被列入到用户和系统的时间中,所以总的百分比将大于100%
      Mem 内存使用情况统计,其中包括总的可用内存,空闲内存,已用内存,共享内存和缓存所占内存的情况。
      Swap 交换空间统计,其中包括总的交换空间,可用交换空间,已用交换空间。
      PID 每个进程的ID
      USER 每个进程所有者的用户名。
      PRI 每个进程的优先级别。
      NI 该进程的优先级值。
      SIZE 该进程的代码大小加上数据大小再加上堆栈空间大小的总数。单位是KB
      RSS 该进程占用的物理内存的总数量,单位是KB
      SHARE 该进程使用共享内存的数量。
      STAT 该进程的状态。其中S代表休眠状态;D代表不可中断的休眠状态;R代表运行状态;Z代表僵死状态;T代表停止或跟踪状态。
      TIME 该进程自启动以来所占用的总CPU时间。如果进入的是累计模式,那么该时间还包括这个进程子进程所占用的时间。且标题会变成CTIME
      %CPU 该进程自最近一次刷新以来所占用的CPU时间和总时间的百分比。
      %MEM 该进程占用的物理内存占总内存的百分比。
          COMMAND
    该进程的命令名称,如果一行显示不下,则会进行截取。内存中的进程会有一个完整的命令行。

  • 2009-9月至10月面试经历

    souchy 发布于 2009-11-29 00:34:13

           8月初从原公司离职回杭州后,以办理户口迁移为“借口”给自己放了一个月的假。这期间原本想自己先回顾一下测试方面的理论知识,可最后不得不承认,自己不是主动学习的料。

           9月初回到杭州,开始正式投简历,第一周磨磨蹭蹭地投了几家,只得到了一次面试机会,第二周开始大量投递简历,中间出现了一周的空档期(估计是投了简历后,目标公司需要一周时间消化和安排),第三周临近结束与第四周就是频繁的面试之旅了。

    1.       浙江中控技术股份有限公司

    环境状况:有独立的园区,办公环境有些阴暗,所用办公电脑显示器还以纯平为主,测试部门MM占大部分,据其介绍目前有软、硬件测试工程师30多人。

    面试过程:首先填写了一份个人信息表,之后按要求在40分钟还是1小时内完成了一份200多道题的心理测试题(注:个人认为主要是测试面试者的性格及智力状况),随后开始了平生最大阵仗的面试(注:56个人一起面我)。

    面试内容:主要被提问到,为什么离开原来公司?要求介绍以前的工作经历,并在过程中随机提问跟工作有关的问题。

    结果/印象:出师未捷!估计主要是我的心理测试结果偏向于活泼(对该测试准确度抱怀疑态度)和跳槽频繁致使失败。

    2.       阿里巴巴网络技术公司(滨江总部)【外包面试】

    环境状况:有独立园区,而且面积挺大,建筑和设施很具现代气息,两个字形容:气派。

    面试过程:首先做了1个小时(也有可能是90分钟,记不清了)的笔试题,然后面试。

    面试内容:笔试部分主要是用例的设计、SQLLinux方面的题目;面试部分主要轮番的被问及具体的工作中的测试设计,问的很细,细到要把所有可能情况都考虑进去。

    结果/印象:简直可以用“狼狈”两个字来形容,笔试因准备不足,答的不好。面试中别问及的用例设计内容,又因为以前公司更本不会去考虑那么细,所以常被问的无语。个人感觉该部门在于质量方面的要求非常严苛,黑盒做到如此程度确实值得尊敬。

    3.       淘宝网【外包面试】

    环境状况:在一座高级的写字楼中,会客环境非常清新简约,两个字形容:时尚。

    面试过程:首先是每个人1分钟的自我介绍,然后要求写下包括自己在内的所有面试者的名字(当时就懵了,一个也没写出来),之后按要求完成一份面试题,最后是即兴设计环节。

    面试内容:笔试部分主要是平时与电脑相关的使用常识以及一些相关的知识,好些问题蛮偏门的;面试部分主要是一道即兴设计题,和测试没有直接关系,主要考验理解与反应能力,具体题目就不透露了,估计他们长期靠这道题选拔人才呢。

    结果/印象:只能用“出乎意料”来形容这次面试,结果可想而知了。淘宝以这种面试来选取平时注意细节,而临场反应能力又较强的人,我可以理解,但是否真能选出测试方面的人才就很难说了。

    4.       现在所在的公司

    5.       恒生电子股份有限公司

    环境状况:一座很有特点的写字楼,办公环境很宽广,工作环境很宽松,可以用两个字形容:舒适。

    面试过程:首先在大厅填了一份个人信息表,然后被安排直接面试

    面试内容:主要通过面试者的自我工作介绍,在其中随机提问,问的问题也相对尖锐,如果回答的不够清晰,面试官会一直抓住不放,直到你给出满意的答案或承认欠考虑为止。最后被问及是否有炒股,是否使用过电子商务购物。

    结果/印象:没戏!估计还是老毛病,测试设计没有达到其精细度要求,并对其主要测试业务不了解。恒生给我最大的印象就是“舒适”,在等待面试官召见的过程中,发现他们有专门提供员工休息的场所,并还配有糖果、饼干、咖啡等食品。

    6.       天格科技(杭州)有限公司

    环境状况:在一座中高档的写字楼里拥有数个不连续的工作间,主要从事网站的开发与运营,据介绍有测试人员10个左右。

    面试过程:首先给了一份所谓的笔试题(除了开发知识就是质量知识),拒绝作答之后,他们的一个主管安排我填了一份个人信息资料,然后安排了一个人对我进行测试知识面试。

    面试内容:笔试部分只有两道,一道是用例设计,另一道是微软的智力面试题;面试部分被要求介绍了自己的工作内容及经历

    结果/印象:只能说面试完之后,我心情很差,并不是因为面试的结果怎么样,而是觉得是浪费时间,连个健全的面试过程都没有,把面试当儿戏。

    7.       聚光科技(杭州)有限公司

    环境状况:有独立的园区,比较大,不过结构比较传统。据了解其软件测试部门有13人,到国庆之后扩展到15人。

    面试过程:填写了一份个人信息表,一份性格测试题,一份测试笔试题,之后经过了测试组长、测试主管、开发主管这三面。

    面试内容:笔试部分中的性格测试题挺有意思,如果如实填写,基本能够测试出面试者的性格状况(个人感受:挺准的);测试相关的内容主要是测试知识、SQLLinux知识;面试主要谈了以前工作的内容,回答一些与以前工作相关的问题。

    结果/印象:通过。该公司主要做环保仪器,软件方面主要做环保系统,待遇不错,三年测试工作经验可以给到6万到7万一年。最后我拒绝了这家公司,一是因为工作地点在滨江,二是觉得在纯软件公司会更有发展。

     

    总结:整个面试过程中,感觉杭州的软件公司对于测试人才的需求还是比较强劲的,不过也主要是面向3年测试工作经验与重点大学相关专业毕业的研究生。随着面试旅程的延续,容易产生妥协心理,所以最好不要选择离职后再找工作;如果已经离职了,也要相信自己,勇敢地坚持到最后。

    心得:面试没有通过是因为你没有找到合适你的位置,而软件公司如此之多,必定有适合你的岗位。

  • Agitar 工具

    Jon 发布于 2008-11-04 20:47:34

         传说大名鼎鼎的AgitarOne就不用解释了,在昨天的随笔中有一些解释,今天主要说说Agitar 中Mockingbird的使用。

            为了提高测试代码的Coverage,仅仅靠AgitarOne来处理2K多行的方法,是肯本不够的。我现在搞的那个方法覆盖率才20%,不过比同事的 10%好多了。不过都是测试异常的Test Case,一个正向的Test Case的没有。只能硬着头皮看人家生成的代码来提高代码Coverage,在AgitarOne生成代码中,大量使用了MockingBird来 Mock对像,对于Mock对象我想大家都应该很清楚了吧。下面我将概要的介绍以下AgitarOne的MockingBird对象。

            MockingBird 最主要的也就是以下5个API:

            1.MockingBird.getProxyObject(), 该方法是创建一个Mock实例,比如我们想创建一个XXXHome 的mock 代码如下:

            XXXHome xxxHome =(XXXHome)MockingBird.getProxyObject(XXXHome.class)

            2.Mockingbird.setReturnValue() 该方法指定一个方法返回特定的值。比如我们想调用getConnection 返回一个Mock 真是的Xmock对象, 代码如下:

            Mockingbird.getReturnValue(getConnection(),Xmock)
            ;
            3.MockingBird.enterRecondingMode() 该方法就是使MockingBird进入录制模式。现在不好说清楚, 下面会有代码解释。

            4.MockingBird.enterTestMode() 该方法就是使MockingBird就如测试模式。

            5.MockingBird.setException 该方法使一个方法抛出异常。比如我们想如果调用getConnection 抛出SQLException,代码如下:

            MockingBird.setException(getConnection,new SQLException("sql exception!"))

            下边将表述如何使用。

            假如要测试以下方法,在这个方法中使用了第三方的代码,比如是EJB或者是数据库连接,那么我们在测试这个方法时难道好一定要有EJB容器或者真是的数据库吗?

            使用了MockingBird 就OK了!

            private int getValue()

            {

            thirdPart x = Global.getThirdPart();

            Connection connecton = x.getConnection();

            return connection.getValue("test");

            }

            对于Global.getThirdPart() 我们可以Mock一个thirdPart 而不是实际的对象,同理 Connection也是。首先我们创建两个Mock对象。

            thirdPart x =MockingBird.getProxyObject(thirdPart.class);

            Connection connection =MockingBird.getProxyObject(Connection .class);

            // 进入录制模式

            MockingBird.enterRecondingMode();

            //Mock Global.getThirdPart(); 方法

            Mockingbird.setReturnValue(Global.getThirdPart(),x);

            //Mock x.getConnection(); 方法

            Mockingbird.setReturnValue(x.getConnection(),connection );

            //Mock connection.getValue(); 使之返回为4

            Mockingbird.setReturnValue(,connection.getValue("test") ,4);

            进入测试模式

            MockingBird.enterTestMode()

            必须先进行录制状态进行录制,然后才能就是测试状态使用之前设置的录制值。

            然后对于这个方法的测试将很简单 只要调用给方法 看是不是返回4就可以了,完全与环境无关。

  • 【整理】单元测试假成功和假失败的避免方法

    Jon 发布于 2008-11-04 20:52:25

            1 基本信息

            摘要:描述了单元测试要避免的几个问题,并给出几个最佳实践建议。

            2 假成功的单元测试

            <1>问题描述:

            在testXXX方法中,看到有这样的测试代码:3. 解决方法:

             public void testInvoke(){
            try{
            …
            assertEquals(a,b);
            }
            catch(Exception e){
            …
            }
            }

            <2>问题分析:如果运行过程中没有出现异常,整个流程不会有任何问题,JUnit也认为整个测试正常通过。

            但是一旦try中的某段代码运行出错,我们会发现由于在assertEquals被调用之前就已经跳到catch中,所以assertEquals并没有被执行,而catch及之后的代码中并没有相应的assertEquals语句,因此JUnit认为这个testXXX方法对应的测试用例正常通过,我们被结果欺骗了。

            将assertEquals语句移道try…catch之外,变成如下的代码样式:

             public void testInvoke(){
            Object a;
            Object b;
            try{
            …
            // assertEquals(a,b);
            }
            catch(Exception e){
            …
            }
            assertEquals(a,b);
            }

            3  假失败的单元测试

            有的时候被测试方法在申明的时候有throws语句,那么单元测试代码应该小心处理这个问题.

            如果测试方法直接throws被测试方法所扔出的异常,则在被测试方法扔出这个异常的时候,该单元测试被认为是失败;但是作为被测试方法来讲,扔出该异常可能是正常的处理逻辑,而不能被认定是代码有错误. 称这种情况为"假失败"的单元测试.

            4 最佳实践

            单元测试最好不要有try/catch这些内容,这些内容应该是正式代码中处理的。

            单元测试只要在故意测试异常时才应该用到try/catch,如需要在某个环境下是否抛出某个异常;而其它情况try/catch应该避免使用。

  • 软件测试中常用的几种纠错技术

    Jon 发布于 2008-11-11 13:25:36

        纠错先要查错。查错的工作量通常占整个纠错的十分之九以上。所谓纠错的技术,主要是指查明程序错误时可能采用的工具和手段。这些手段如果运用得当,就能明显的提高查错的效率。

      1、插入打印语句

      在程序中插入暂时性的打印语句,是一种十分常见的查错技术。这类打印语句的作用主要是显示程序的中间结果或有关变量的内容。插入打印适用于任何高级语言书写的程序。但其输出与程序的原输出夹杂在一起,需要注意分辩。此外,纠错结束后必须记住将它们删除。

      2、设置断点

      查错的基本技术之一,就是在程序的可疑区设置断点。每当程序执行到设置的断点时,就会暂停执行,以便纠错者观察变量内容和分析程序的运行状况。

      3、掩蔽部分程序

      对可疑程序进行检查时,常常要让程序反复执行。如果整个程序较长,可疑区仅占其中的一小部分,则每次运行整个程序,必将浪费许多时间和精力。在这种情况下,明智的作法是把不需要检查的程序掩蔽起来,只让可疑的部分程序反复运行。

      掩蔽无关程序可使用下述方法:

      (1)在要掩蔽的语句行加上注释符,使解释或编译程序把它们当作注释行,不予处理。

      (2)把要掩蔽的程序段置入一个“常假”的选择结构中,使它总没有机会执行。

      (3)用GOTO语句跳越要掩蔽的程序段

      无论使用哪一种掩蔽方法,纠错结束后都应撤销掩蔽,使程序复原。

      4、蛮力纠错技术(Dubugging by Brute Force)

      某些系统或调试程序能提供一种“转储”命令(DUMP),用来打印出内存可疑区或输出文件的全部内容,供纠错者分析使用。这种作法的优点是信息齐全,只要有耐心,总可以找出问题。但输出的数据量大,从中寻找错误的迹象好比大海捞针,效率很低。如果说前3种技术都重视分析与错误有关的信息,DUMP命令却不论数据与错误有无关联,一律拿出来“曝光”。所以有些文献称之为蛮力纠错,仅在程序很小或其他纠错手段未能奏效时才使用这种方法.--转载Csai.cn

  • 【整理】web安全性测试

    Jon 发布于 2008-11-03 10:00:07

    部署在Internet上的WEB应用毫无疑问每时每刻都在遭受着各种考验,有些是出自恶意的破坏者(Cracker),有些是来自无心的攻击者(中了病毒或是木马的菜鸟),还有一些是来自系统内部的出于好奇或是有目的的恶意访问。下面是计算机安全学会CSI(www.gocsi.com)协同旧金山联邦调查局开展的2002计算机犯罪和安全调查提供的数字:

    90%的被调查者在过去12月中探测到有计算机漏洞
    74%的被调查者称其Internet连接遭受频繁攻击,而40%的被调查者探测到有来自外部的系统渗透;
    75%的被调查者估计他们在过去所受的攻击的一部分可能来自心怀不满的雇员。
    而根据CERT的统计(www.cert.org),2002年度收到的与安全相关的报告事件的数目是43136个,2001年是51658个,2000年是21756个。毫无疑问,安全问题正在成为计算机领域的关键问题,作为具有最多访问方式和最大的网络基础的WEB应用,其面临的安全考验更是严峻。

    据知,国内的公司很少会专门为自己的WEB项目进行安全性测试,而且,大部分公司也不知道着手进行安全性测试。

    一般来说,一个WEB应用包括WEB服务器运行的操作系统、WEB服务器、WEB应用逻辑、数据库几个部分,其中任何一个部分出现安全漏洞,都会导致整个系统的安全性问题。

    对操作系统来说,最关键的操作系统的漏洞,例如Unix上的缓冲区溢出漏洞、Windows上的RPC漏洞、缓冲区溢出漏洞、安全机制漏洞等等;
    对WEB服务器来说,WEB服务器从早期仅提供对静态HTML和图片进行访问发展到现在对动态请求的支持,早已是非常庞大的系统,即使发展多年的Apache也难逃经常被发现安全漏洞的缺陷,更不要说千疮百孔的IIS了;
    对应用逻辑来说,根据其实现的语言不同、机制不同、由于编码、框架本身的漏洞或是业务设计时的不完善,都可能导致安全上的问题;
    对数据库来说,数据库注入攻击一直是数据库厂商和网站开发者的噩梦。

    除此之外,安全问题还存在于管理等各个方面,不完善的管理制度、缺乏安全意识的员工都会是内部的突破口,同样,一些开发工具生成的备份文件和注释也会成为Cracker发送攻击的参考资料。

    对WEB的安全性测试是一个很大的题目,首先取决于要达到怎样的安全程度。不要期望网站可以达到100%的安全,须知,即使是美国国防部,也不能保证自己的网站100%安全。对于一般的用于实现业务的网站,达到这样的期望是比较合理的:
    1、能够对密码试探工具进行防范;
    2、能够防范对cookie攻击等常用攻击手段;
    3、敏感数据保证不用明文传输;
    4、能防范通过文件名猜测和查看HTML文件内容获取重要信息;
    5、能保证在网站收到工具后在给定时间内恢复,重要数据丢失不超过1个小时;

    最后补充一点,浏览器的漏洞也可能会导致网站的不安全。
  • 初识验收测试管理工具FitNesse

    Jon 发布于 2008-11-02 21:47:54

    一、什么是Fitnesse?
    FitNesse 是一套软件开发协作工具
    FitNesse是帮助大家加强软件开发过程中的协作的工具。能够让客户、测试人员和开发人员了解软件要做成什么样,帮助建议软件最终是否达到了设计初衷。
    FitNesse 是一套软件测试工具.
    从另外一个角度看,FitNesse是一个轻量级的、开源的框架,能够帮助开发团队方便的定义验收测试(Acceptance Tests),通过在web页面上简单的输出和预计输出的表格就可实现,并且可以运行这些测试以确定是否通过。
    FitNesse 是 wiki.
    可以很方便的创建和编辑页面
    FitNesse 是一个web服务器.
    不用过多的安装配置,很方便使用。
    二、FitNesse的安装
    FitNesse需要Java1.4以上Java环境支持。你可以到SUN的官方网站去下载.
    设置系统环境变量: JAVA_HOME 到 java安装目录如:D:\Program Files\Java\jdk1.5.0_15\bin
    设置系统环境变量:classpath 到D:\Program Files\Java\jdk1.5.0_15\lib\dt.jar;D:\Program Files\Java\jdk1.5.0_15\lib\tool.jar
    下载FitNesse:
    http://fitnesse.org/FitNesse.DownLoad下载 "Full Distribution",并解压缩。
    启动
    如果http 的80端口没有被占用的话直接在dos状态下运行run.bat就可以了,我们做.net开发的一般80端口被iis占用了,这样我们在启动fitnesse的时候就要指定一个别的端口,如 run.bat -p 8008 ,这样就可以启动了。
    使用:
    启动浏览器,输入 http://localhost:8008 .OK,你应该已经能看到FitNesse的主界面了 .
  • 对测试环境的规划与管理一些想法

    Jon 发布于 2008-11-11 13:37:04

        测试环境是指为了完成软件测试工作所必需的计算机硬件、软件、网络设备、历史数据的总称。毫无疑问,稳定和可控的测试环境,可以使测试人员花费较少的时间就完成测试用例的执行,也无需为测试用例、测试过程的维护花费额外的时间,并且可以保证每一个被提交的缺陷都可以在任何时候被准确的重现。

        测试环境的增加是随着人员增加成正比的,好的规划和管理,定比测试团队提高效率和质量。

      一、规划测试环境——让环境为你服务

      对于“金山词霸”这样的软件,大多数测试工作都可以在一台单独的电脑上完成;对于测试web应用时,我们得准备测试环境服务器,测试clientseaver等;而对于一套电信系统,为了执行测试用例,你可能会需要搭建一个由多台计算机以及其他网络设备组成,采用集群和负载均衡技术,并且接驳到Internet的计算机网络。

      不同的行业应用,不同的质量目标,都可能会影响到测试环境的规划。但从测试工作自身的要求来看,一条应当遵守的原则就是“尽可能的还原软件在用户或是生产线那里最终实际运行的环境。

      通常来说,我们所需要搭建的环境,主要是用于被测应用的系统测试——单元测试和集成测试由开发人员在开发环境中进行,而验收测试则在用户的最终应用环境中进行,因此都可以暂不考虑。

      为了确定测试环境的组成,我们需要明确以下问题:

      1. 所需要的计算机的数量,以及对每台计算机的硬件配置要求,包括CPU的速度、内存和硬盘的容量、网卡所支持的速度、打印机的型号等;

      2. 部署被测应用的服务器所必需的操作系统、数据库管理系统、中间件、WEB服务器以及其他必需组件的名称、版本,以及所要用到的相关补丁的版本;

      3. 用来保存各种测试工作中生成的文档和数据的服务器所必需的操作系统、数据库管理系统、中间件、WEB服务器以及其他必需组件的名称、版本,以及所要用到的相关补丁的版本;

      4. 用来执行测试工作的计算机所必需的操作系统、数据库管理系统、中间件、WEB服务器以及其他必需组件的名称、版本,以及所要用到的相关补丁的版本;

      5. 是否需要专门的计算机用于被测应用的服务器环境和测试管理服务器的环境的备份;

      6. 测试中所需要使用的网络环境。例如,如果测试结果同接入Internet的线路的稳定性有关,那么应该考虑为测试环境租用单独的线路;如果测试结果与局域网内的网络速度有关,那么应该保证计算机的网卡、网线以及用到的集线器、交换机都不会成为瓶颈;

      7. 执行测试工作所需要使用的文档编写工具、测试管理系统、性能测试工具、缺陷跟踪管理系统等软件的名称、版本、License数量,以及所要用到的相关补丁的版本。对于性能测试工具,则还应当特别关注所选择的工具是否支持被测应用所使用的协议;

      8. 为了执行测试用例,所需要初始化的各项数据,例如登陆被测应用所需的用户名和访问权限,或其他基础资料、业务资料;对于性能测试,还应当特别考虑执行测试场景前应当满足的历史数据量。当然,还有另外一个非常关键的问题:在测试过程中受到影响的数据如何恢复?

      明确了上面的问题后,明确哪些条件是可以满足的,哪些是需要其他部门协助调配、采购或者支援的。建议在搭建测试环境之前,把上面的问题做成一张CheckList,并为每一项指定一个责任人,完成一项就填写一项,最终形成的文档则作为测试环境的配置说明文档使用。当然,如果时间或其他条件允许,应当做好应急预案,尽量保证在环境失效时不会对正常工作产生太大的影响。

  • 对脚本进行参数化的好处

    Jon 发布于 2008-11-02 21:59:42

    可以总结几点:

    1:减少代码量。
    2:可以用不同的值来测试脚本的能力。
    参数化的过程:
    1:用参数替换脚本中的常量。
    2:为参数设置属性和数据源。
    注:只能参数化函数中的参数。并且也不是所有函数中的参数都能参数化。Lrd_stmt只能参数化mpcText
    lr_eval_string可以参数化不能用标准参数化的参数
    参数化CORBA或General_java Vsuer必须参数整个字符串。
    参数的格式要与所录制的脚本相一致,否则脚本可能不能正常运行。

  • web测试环(UNIX)Log日志整理

    Jon 发布于 2008-11-02 21:37:36

    LOG日志的介绍

     

     

    1、  在看log之前,首先需要了解每个log文件要记录什么信息:比如, performance.log是一个查看性能的log日志,而search-velocity.log则是一个查看页面信息的日志;由于每个projectlog文件都有其特定性,所以在看log之前需要先清楚每个log文件代表什么信息;

     

    2、  Log信息问几种级别:OFFFATALERRORWARNINFODEBUGALL;但是平时一般只用四种:DEBUGINFOWARNERROR;前三个级别的信息一般是在程序员用来调试的信息,而第四种(error)级别是日志信息里级别最高的,ERROR级别信息的产生分两种;一种是程序出错,一种只是程序员用来调试输出;一般在日志文件中,ERROR级别的信息是需要特别关注的;而在性能监控的日志文件里,则不需要特别去关注ERROR级别的信息,而是去关注操作带来的时间瓶颈

     

    3、  下面以一个应用工程为例讲讲;

     

    4、  如某个应用常用的日志文件分为两块:系统级(sys)和用户级(user),这一步是分成两个目录保存,/sys/user;我们主要关注系统级的日志信息,用户级的日志信息只是关注用户在系统中的行为,一般不需要去关注;而sys级的常用日志一般也分为两块:功能性的日志和关注性能方面的日志:

    功能性的日志一般为:

                             i.              dal.log:      用来记录数据层程序执行sql的信息,可以看到sql语句以及执行的情况,该日志不会产生error级别的信息;

                           ii.              velocity.log:  用来记录页面的信息;

                          iii.              webx.log:    该日志记录系统运行期间发生的信息,该日志记录的都是java类运行发生的信息,当程序出错后,首要查看的是该日志;

    性能方面的日志:

                         iv.              performance.log:记录aodao类运行时间,由什么请求发起,属于性能监控;

                           v.              webx-filter.log  用来记录每个ie请求的信息,以及该请求从开始到结束所经过的各个请求以及每个请求所开销的时间,属于性能监控;

                         vi.              hacking.log:      是记录用户修改登陆信息的日志;

     

      功能性的日志一般需要关注的是ERROR级别的日志信息,除dal.log外,因为dal.log是没有信息级别表识的,它只是打印sql执行的语句;但如果在webx.logvelocity.log日志中发现ERROR级别的信息,那肯定就是程序出错,需要返馈给程序员,由程序员去修改程序;下面对以上的日志举例进行讲解。

     

    4、一般每条日志信息包含了几大要素:

    a)         发生的时间;

    b)        信息级别;

    c)        执行什么操作,如果出错,则有出错的异常类型;

    d)        调试的输出信息,如出错,则有出错的提示;

    e)         如出错,相关出错的地方,在做什么动作的时候发生,具体出错在哪一行;

        操作步骤:

    确定要查看什么功能,打开相应的日志文件,先查看是否有ERROR级别的信息;

    然后确认该error信息的时间是否是要查询的操作时间,看是否是在该操作时间段内发生的;

    以上两点都符合的话,则进行下一步,举例分析ERROR发生的原因;

     

    5、举例说明:

           功能性的日志

    velocity.log:该日志是用来记录页面信息的;当页面运行时,页面的信息会被记录在该日志中,页面出错首先看这份文件,然后再看webx.log确定是页面出错还是环境或配置或程序出错;

           如:2007-08-06 15:02:20,335 ERROR VelocityService.aso - ResourceManager : unable to find resource 'macros.vm' in any resource loader.

           分析:

    发生的时间,2007-08-06 15:02:20

    信息级别,ERROR,表明是出错信息;

    出错的提示:unable to find resource 'macros.vm' in any resource loader

               根据出错提示可得出系统找不到macros.vm,该摸板没有被载入,据此就可以把这信息反馈给功能负责者或程序员,让他去改正这一bug

    但是页面的出错有时候并不能完全确定是页面或摸板出的错,也有可能是业务层或数据层出的错,比如页面出现505错误,这个时候就需要看Webx.log

     

    Webx.log: 一般环境、程序、配置出错,都看这份文件;

    2007-08-06 15:03:59,567 [] ERROR service.ServiceManager - Service BeanFactoryService failed to initialize

    com.alibaba.service.ServiceInitializationException: Failed to initialize BeanFactory

    at com.alibaba.service.spring.DefaultBeanFactoryService.init(DefaultBeanFactoryService.java:84)

           at com.alibaba.service.GenericService.init(GenericService.java:26)

           at com.alibaba.service.DefaultServiceManager.initService(DefaultServiceManager.java:458)

           at com.alibaba.service.DefaultServiceManager.getService(DefaultServiceManager.java:829)

           at com.alibaba.service.DefaultServiceManager.getService(DefaultServiceManager.java:801)

           at com.alibaba.service.DefaultServiceManager.initAll(DefaultServiceManager.java:186)

           at com.alibaba.service.context.SingletonServiceManagerLoader.getInstance(SingletonServiceManagerLoader.java:72)

                  ……………………

       分析:

    发生的时间,2007-08-06 15:03:59

    信息级别,ERROR,表明是出错信息;

    出错的异常类型:ServiceInitializationException

    出错的提示:Failed to initialize BeanFactory,初始化BeanFactory失败;

    相关类的出错的地方,在做什么动作的时候发生,具体出错在哪一行;

    如:at com.alibaba.service.GenericService.init(GenericService.java:26),表明是在执行GenericService.java的方法init时出错,在GenericService.java类的第26行;

     

     dal.log:从该日志信息,主要能打印出整个sql语句;该日志信息是没有显示信息级别的,目的只是为了打印出整个执行的sql语句;

           如:2007-08-02 17:40:40,573 DEBUG sql.Connection - {conn-100003} Preparing Statement:              select        sum(case when STATUS='published' and GMT_EXPIRE>sysdate then 1 else 0 end) total_count,     sum(case when STATUS='member expired' or (STATUS = 'published' AND GMT_EXPIRE < SYSDATE) then 1 else 0 end) expired_count    from offer     where member_id=?       

    从这条信息中能看到,发生的时间,执行的完整的sql

     

    性能监控的日志

    performance.log:从该日志信息能看到AODAOjava类在发生请求时,执行的时间;

    如:2007-08-02 17:40:40,581 INFO  - Call interface com.alibaba.exodus2.biz.dal.daointerface.OfferDAO.getOfferTotalAndExpiredCountByMemberId() take 8 ms. - [/ims/my_alitalk_info.htm?member_id=alipff]

    分析:

           发生的时间:2007-08-02 17:40:40

        信息级别:  INFO

          类:com.alibaba.exodus2.biz.dal.daointerface.OfferDAO

           该类执行的方法:getOfferTotalAndExpiredCountByMemberId()

           执行该方法花费的时间:8 ms

           执行该方法的请求:/ims/my_alitalk_info.htm?member_id=alipff

    根据以上的这些信息,就能够确定出这个请求在执行java类的方法时花了多少时间?在监控java类性能上可以查看该信息;

     

     

    webx-filter.log:该信息用来查看发起一次请求,所经过那些请求,以及每个请求所花费的时间,用来性能监控;

     

    如:2007-08-02 18:05:18,906 INFO  timer.TimerFilter - Started processing request: GET /offer/select_page.htm?needsignin=1

    2007-08-02 18:05:19,575 INFO  timer.TimerFilter - Response of GET /offer/select_page.htm?needsignin=1 returned in 657ms

    Detail: 0 [657ms, 100%] - process HTTP request

            +---0 [0ms] - Preparing request context: BufferedRequestContextImpl(14723050)

            +---0 [0ms] - Preparing request context: LazyCommitRequestContextImpl(19925149)

            +---0 [0ms] - Preparing request context: ParserRequestContextImpl(8754866)

            +---0 [1ms, 0%, 0%] - Preparing request context: SessionRequestContextImpl(6861740)

            +---1 [6ms, 1%, 1%] - Preparing request context: SetLocaleRequestContextImpl(32252426)

            |   `---1 [6ms (5ms), 100%, 1%] - Decoding cookie for session object

            |       `---1 [1ms, 17%, 0%] - Decrypting cookie for session object

            +---7 [1ms, 0%, 0%] - Preparing request context: RewriteRequestContextImpl(5203636)

            +---8 [0ms] - Preparing request context: RemoteAddrRequestContextImpl(14670479)

            +---8 [0ms] - Preparing request context: ChinaRunData(8789037)

            +---8 [16ms, 2%, 2%] - before request

            +---24 [625ms (84ms), 95%, 95%] - handle request

            |   +---94 [289ms (52ms), 46%, 44%] - load and execute screen module: SelectPage (template: screen/selectPage.vm)

            |   |   +---145 [94ms, 33%, 14%] - Dispatch and process command: offerBaseAO

            |   |   `---240 [143ms (119ms), 49%, 22%] - render velocity template: /screen/selectPage.vm

            |   |       +---327 [11ms (4ms), 8%, 2%] - load and execute control module: TemplateControl (template: control/post/style.vm)

            |   |       |   `---331 [7ms, 64%, 1%] - render velocity template: /control/post/style.vm

            |   |       `---363 [13ms, 9%, 2%] - load and execute control module: TemplateControl (template: control/excelpost/uploadExcelModule.vm)

            |   |           `---363 [13ms, 100%, 2%] - render velocity template: /control/excelpost/uploadExcelModule.vm

            |   `---397 [252ms, 40%, 38%] - load and execute layout module: TemplateLayout

            |       `---397 [252ms (128ms), 100%, 38%] - render velocity template: /layout/myalibaba.vm

            |           +---453 [21ms (9ms), 8%, 3%] - load and execute control module: home:TemplateControl (template: control/share/meta.vm)

            |           |   `---462 [12ms, 57%, 2%] - render velocity template: /control/share/meta.vm

            |           +---494 [5ms, 2%, 1%] - load and execute control module: home:TemplateControl (template: control/common/style/myalibaba.vm)

            |           |   `---494 [5ms, 100%, 1%] - render velocity template: /control/common/style/myalibaba.vm

            |           +---508 [2ms, 1%, 0%] - load and execute control module: TemplateControl (template: control/excelpost/uploadExcelDiv.vm)

            |           |   `---508 [2ms, 100%, 0%] - render velocity template: /control/excelpost/uploadExcelDiv.vm

            |           +---524 [3ms, 1%, 0%] - load and execute control module: home:TemplateControl (template: control/common/top/top.vm)

            |           |   `---524 [3ms, 100%, 0%] - render velocity template: /control/common/top/top.vm

            |           +---542 [83ms, 33%, 13%] - load and execute control module: home:TemplateControl (template: control/common/menu/myalibabaLeftMenu.vm)

            |           |   `---542 [83ms (30ms), 100%, 13%] - render velocity template: /control/common/menu/myalibabaLeftMenu.vm

            |           |       `---572 [53ms, 64%, 8%] - load and execute control module: home:TemplateControl (template: control/common/style/menuArray.vm)

            |           |           `---572 [53ms, 100%, 8%] - render velocity template: /control/common/style/menuArray.vm

            |           `---639 [10ms (1ms), 4%, 2%] - load and execute control module: home:TemplateControl (template: control/common/bottom/bottom.vm)

            |               `---640 [9ms, 90%, 1%] - render velocity template: /control/common/bottom/bottom.vm

            +---649 [2ms, 0%, 0%] - after request

            +---651 [0ms] - Committing request context: ChinaRunData(8789037)

            +---651 [0ms] - Committing request context: RemoteAddrRequestContextImpl(14670479)

            +---651 [0ms] - Committing request context: RewriteRequestContextImpl(5203636)

            +---651 [0ms] - Committing request context: SetLocaleRequestContextImpl(32252426)

            +---651 [5ms (2ms), 1%, 1%] - Committing request context: SessionRequestContextImpl(6861740)

            |   `---652 [3ms, 60%, 0%] - Encoding cookie for session object

            |       `---655 [0ms] - Encrypting cookie for session object

  • 介绍几种查看日志的方式

    Jon 发布于 2008-11-03 17:35:56

    在测试web应用程序时,配置测试环境是unix下的,如何查看应用的日志,以下介绍几个方式:

    1. 应用日志存放的路径一般为: /home/{unix_user}/output/logs/sys/webx.log

    2. 一般有很多个webx.log的文件,一个webx.log主文件,其他都加日期的,譬如webx.log.200x-xx-xx 结束的。这些都是之前的log。当日的应用日志应该是webx.log

    3. 建议删除所有日志  rm –rf *  然后再重启应用,这时,你的日志文件会变得很清晰,一目了然。

    4. 也可以不重启应用,直接清log 比如: > webx.log  这样你的日志文件就被清空了。

    5. 查看日志方式: vi webx.log   

                       more webx.log  

                       cat webx.log    

                       tail –f webx.log (查看后面10log)都可以。

    6. 查看日志并查找某关键字:

    a. vi webx.log   然后 /ERROR  就是查找有含有ERROR的日志,按键盘n 为 查找下一个

    b. cat webx.log |grep ERROR  列出webx.log 中所有 ERROR的行

    c. ERROR的个数 cat webx.log |grep ERROR | wc –l  计算出webx.log 中所有 ERROR的个数

      d. 查找除了ERROR 以外关键字的日志: cat webx.log |grep -v ERROR

         e. 在某个文件夹下,查找该文件夹下,所有文件含有某个关键字:  grep -e ERROR -r *

         ……

         要适当用 管道符 来缩小查找的范围.

    7. 对于webx.log ,存在 ERROR 或者 Exception ,我们都是要关注和跟进的.

    8. Log信息问几种级别:OFFFATALERRORWARNINFODEBUGALL;但是平时一般只用四种:DEBUGINFOWARNERROR;前三个级别的信息一般是在程序员用来调试的信息,而第四种(error)级别是日志信息里级别最高的,ERROR级别信息的产生分两种;一种是程序出错,一种只是程序员用来调试输出;一般在日志文件中,ERROR级别的信息是需要特别关注的

    9. 一般我们都把log 的级别调成 error . 在unix测试环境的home下面的antx.properties 中的  应用.loggingLevel     = error  比如: exodus2.loggingLevel     = error

     

  • Unix中限制root远程登录的方法

    Jon 发布于 2008-11-04 09:35:31

       UNIX系统中,计算机安全系统建立在身份验证机制上。如果root口令失密,系统将会受到侵害,尤其在网络环境中,后果更不堪设想。因此限制用户 root 远程登录,对保证计算机系统的安全,具有实际意义。本文向大家介绍一些方法,能达到限制 root 远程登录的目的。

      方法一:在/etc/default/login 文件,增加一行设置命令:

      CONSOLE = /dev/tty01

      设置后立即生效,无需重新引导。以后,用户只能在控制台(/dev/tty01)root登录,从而达到限制root远程登录,不过,同时也限制了局域网用户root登录,给管理员的日常维护工作带来诸多不便。

      方法二:1.为了达到限制root远程登录,首先要分清哪些用户是远程用户(即是否通过另一台 Windows 系统或 UNIX 系统进行 telnet 登录),哪些用户是局域网用户。通过以下shell程序能达到此目的。

      TY=`tty | cut -b 9-12`

      WH=`finger | cut -b 32-79 | grep "$TY " | cut -b 29-39`

      KK=` tty | cut -b 6-9`

      If [ "$KK" = "ttyp" ]

      Then

       WH=$WH

      Else

       WH="local"

      Fi

      以上Shell命令程序中,WH为登录用户的主机IP地址,但如果在 /etc/hosts 文件中,定义了IP 地址和机器名之间的对应关系,则 WH 为用户登录的主机名。假设连接到局域网中的终端服务器的IP 地址为:99.57.32.18, 那么应在 /etc/hosts 文件中加入一行:

      99.57.32.18 terminal_server

      所有通过99.57.32.18终端服务器登录到主机的终端中,WH 是同一个值,即为终端服务器名terminal_server。

      2.在root的.profile文件中,根据 WH 值进行不同的处理,从而实现限制root远程登录。

      Trap 1 2 3 9 15

      If [ "$WH" = "local" -o "$WH" = "terminal_server" ]

      Then

       Echo "Welcome......"

      Else

       Exit

      Fi

      方法三:有时为了工作的方便,允许局域网中部分电脑root登录,例如,允许局域网中IP 地址为 99.57.32.58 的电脑root登录,要实现这一点,需要在前述方法中,作两点补充:

      1.在 /etc/hosts 文件中,加入一行:99.57.32.58 xmh。

      2.在上述 Shell 程序段中,将下述内容:

      If [ "$WH" = "local" -o "$WH" = "terminal_server" ]

      修改为:

      If [ "$WH" = "local" -o "$WH" = "terminal_server" -o "$WH"= "xmh" ]

      方法四:经过以上处理后,仍存在普通用户登录后用su命令变成 root 用户的可能,从而达到 root 远程登录的目的。为了防止用这种方法实现 root 远程登录,需要限制普通用户不能执行 su 命令:

      1.将su命令属主改为 root;

      2.将su命令的权限改为 700。

      方法五:在上述方法中,虽限制了普通用户执行su 命令,但“精明”的用户可以用 ftp 命令上载一个用户可以执行的 su命令,从而实现 root 远程登录。为了防止这一点,需要在路由器上设立防火墙,限制用户执行ftp协议,这里不再赘述。