qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

Quality Center引发的测试管理思考

 QC是很出名的强大的管理工具,很多测试爱好者都追逐着它,学习怎么使用它,今天我写这篇文章不是讲解如何使用,而是通过分析这个管理软件来看看测试管理的思想,其实工具不仅给我们带来了效率和便捷,更多的是给我们流程上的指导,如果你深刻理解你会发下QC就给了我们一个很好的测试管理指导。好了,废话不多说,我们就来看看它吧!

  首先我们来看下QC的整体管理流程图,如下图:

  看似简单的图,其实蕴藏着很多内容。QC从前期的需求管理到测试点的提取,再到测试用例的形成,测试的执行,以及缺陷的跟踪管理全部包括了。也许这时候你会问,这有什么新鲜的现在好多管理工具都是这个流程,包括我们现在的流程也是如此,有什么炫耀的?

  ok,那咱们就来点真格的。如果你熟悉QC,你会发现QC中的需求是能和测试用例以及相应的Bug关联的(这就是QC中需求转化为测试的功能),就这点来说非常实用。为什么这么说呢?大家知道我们基本面对的都是web产品,他的特点就是变化快,更新换代更快,也许这个版本就和上个版本有天壤之别。这是web产品的特点我们无法改变,但我们可以改变自己来适应他。 说白了,就是按照QC的思想,我们来建立需求与测试用例以及Bug之间的关联。这样做的好处就是当需求发生变更时,能找到变更的需求涉及到的用例以及Bug。如果你买不起昂贵的QC我们可以这样来做(个人观点,如有雷同,纯属巧合):

  1、需求格式规范化,在需求中给出功能模块划分图,以及各个功能的优先级。这样做的好处是轮到tester分工时可以根据需求中的功能模块图来划分,这样的话就能避免重复或遗漏功能,同时根据产品人员提供的优先级我们可以直接拿到用例中来,这样的话需求和用例间就建立了关联,当然这个关联关系很弱,还有待改进。

  2、测试用例的规范,测试用例的原子化。其实这里仍然是QC中的体现,在上一条中我们提到了功能模块划分,那么在测试用例中就根据功能模块的再次细分,分解到原子状态。为什么这么做呢?其实功能模块的划分相当于QC中的测试点,而我们继续划分则分解成了详细的用例,那么到这一步,我们就建立了需求—>测试点—>测试用例之间的关联了。

  3、接下来这是怎么把用例与Bug关联起来?可以使用TestLink来管理用例,

  TestLink是一个开源的测试管理工具能够和JIRA等缺陷管理工具集成,这样就建立了用例与Bug之间的关联。说实话,TestLink是个不错的测试管理工具,尤其是对于无法购买昂贵的商业工具的工具来说,但TestLink的易用性我实在不敢恭维啊,用起来超级无敌不顺手,但时间长了也就习惯了。

  ok,到这里我们仿照QC的思想建立了一个基础的管理流程,但这里流程还需要加强,如何提高他们之间的关联度还是个问题,尤其是当需求发生变化时,如何能在最短时间内找到涉及的用例以及Bug。

  下面我们继续关注QC用例方面的思想。我们知道QC能够很好的和LoadRunnerQTP等HP的测试工具结合,在QC中去调用和管理这些工具,那么这里就设计到一个用例格式以及用例重用的问题。对于测试来说用例是至关重要的,所以用例的规范也是很有必要的。那么这里我就UI Check List、功能测试用例和性能测试用例来说下。

  我个人一直认为,对于UI方面以及经常要测试重用性非常高的模块,我们可以设计成一个Check List,这样的话看起来方便,执行起来快捷,维护成本也低,拿来就能用,是个很好的方法。

  对于功能测试用例以及性能测试用例,我个人比较喜欢把功能测试用例的格式进行扩充转化为性能测试用例,这样他们之间的格式和关联性方面就不会有太大的差异了。下面的图是我设计的一个性能测试用例,其实它也是源于LoadRunner这个工具的思想演变而来的。

  仔细观察你会发现,这个用例就是功能测试用例的演变,而且非常符合LoadRunner的测试流程,从用例的信息到脚本设计到场景设计再到执行结果能很好的体现出来。而且就测试结果也能简单明了的反应出来,个人感觉看上去很舒服,O(∩_∩)O~

  哦了,写的也不少了,最后我们以QC的自定义功能来结束这篇文章。QC比其他测试工具更受测试人员喜欢的原因之一就是因为他有强大的自定义功能,能对字段、流程等进行自定义,编写代码来完成你想要的各种流程功能。这也就反映了一个道理,任何管理流程都是灵活的,最好的管理流程并不一定适合你,俗话说的好,适合自己的才是最好的。所以,当我们有了一个基本比较完善的测试管理流程后应该慢慢的把他个性化、人性化,而不是一成不变的去死守,加入新鲜元素,加入新的管理理念,给更多人的展示空间才能不断的去促进流程的改进与提高。

posted @ 2012-05-24 09:26 顺其自然EVO 阅读(208) | 评论 (0)编辑 收藏

软件测试质量和效率评价之我见

 写在前面

  近期在研究如何提高软件测试质量和效率,发现要完成这个课题,先要搞清楚如何评价软件测试质量和效率?搜罗了不少资料,木有找到权威的说法,于是结合自己的经验进行了总结,便得此文,愿抛砖与业界朋友探讨。

  软件测试质量和效率评价的范畴

  原本打算将软件测试质量和效率分别评价,但基于软件测试不可穷尽的特点,以及测试时间总是那么紧张的现实,更多的时候我们需要在质量和效率之间加以权衡,在有限的测试时间内得到可用的系统或产品。因此,质量和效率有着相互制约的关系,但对于一个系统或产品的测试来说,质量和效率都是衡量测试是否成功的指标,索性放在一起讨论。

  经过分析,我认为一个测试效果的好坏可以从以下四个大的方面进行评价,如图1:

  1、测试质量评价:对测试产出及结果的评价,评价比重30%

  2、测试效率评价:测试过程中若干项工作的效率评价,评价比重30%

  3、测试文档评价:测试文档质量的评价,包括对测试方案、测试设计、测试用例、缺陷报告单、测试报告等文档的评价。评价比重25%

  4、测试过程评价:对测试中若干过程执行情况及效果的评价,包括文档评审、测试沟通、执行过程等评价。评价比重15%

图1 测试质量和效率评价

  详细评价指标

  详细评价指标参考下表:

表1:测试质量和效率评价指标


 下面详细解读各指标含义及评价标准。

  测试质量评价

序号 评价指标 指标含义指标解读   备注
1有效Bug比例
有效Bug数/总Bug数

所提Bug有效率越高,说明测试工作越有效,所作无用功越少。
不太赞同用Bug数衡量测试质量。
 2 严重Bug比例严重Bug数/有效Bug数 严重Bug所占比例越高,表征测试越有效,对系统帮助越大。  
 3 漏测Bug加权数 漏测Bug*严重系数之和漏测Bug加权数越高,表征测试质量越低。 因项目周期、用户缺陷反馈渠道等问题,漏测Bug的统计可能遇到障碍。

  测试效率评价

序号 评价指标 指标含义 指标解读 备注
 1 工作按计划完成率1-(实际耗时-计划耗时)/计划耗时 工作能够按照计划完成,表征测试过程控制合理,测试效率也得以保证。 前提是需要一份完整且可执行性强的测试计划。
 2 日均有效Bug加权数有效Bug加权数/测试工作日 日均有效Bug加权数越多,表征工作效率越高。  更适合于对个人工作效率的评价,因为需要横向对比。
 3 测试文档生产率文档页数*难度系数/撰写文档耗时 单位时间产出文档越多,表征文档生成效率越高。 文档质量单独评价,见下节。
 4 文档评审效率(评审建议数+问题数)/评审耗时 单位时间内评审出问题越多,表征工作效率越高。 

  测试效率评价

序号评价指标 指标含义 指标解读  备注
 测试计划文档质量测试计划的规范性,及指导测试的作用是否达到。 测试计划体现测试人员对任务量估计、分工及风险的预估能力,文档越规范、越有效,说明文档质量越高,该项工作的质量也越高。 测试文档的质量从某种程度上反映着测试的质量。 
 2测试方案文档质量 测试方案的规范性,及指导测试的作用是否达到。测试方案体现测试人员对项目测试整体的把握能力,文档越规范、越有效,说明文档质量越高,该项工作的质量也越高。  
 3测试设计/用例文档质量 测试设计和用例文档的规范性,及指导测试的作用是否达到。 测试设计和用例体现测试人员的用例设计水平,即解决测试问题的水平,文档越规范、越有效,说明文档质量越高,该项工作的质量也越高。  
 4 缺陷报告单质量缺陷报告单的规范性和有效性。 缺陷报告单越规范、越有效,对测试问题定位帮助越大。  
 5 测试报告文档质量测试报告文档的规范性,及指导测试的作用是否达到。 测试报告体现测试人员归纳、总结测试数据,呈现测试结论的能力。文档越规范、越有效,说明文档质量越高,该项工作的质量也越高。  

  测试过程评价

序号 评价指标 指标含义 指标解读 备注
 1需求&设计评审过程评价 从流程执行的规范性及评审效果两方面评价。评审过程按流程要求进行,评审对需求和设计的帮助越大,说明该项工作质量越高。
 2测试沟通过程评价从测试沟通过程的规范性及沟通结果进行评价。测试沟通按流程要求进行,沟通对形成测试方案和测试设计的帮助越大,说明该项工作质量越高。 
 3测试方案&设计&用例评审过程评价 从测试方案、设计、用例评审的过程及效果两方面进行评价。各评审过程按流程要求执行,评审出的问题越多、帮助越大,说明该项工作质量越高。 
 4 测试执行过程评价从测试执行的规范性及效果进行评价。测试执行规范,分工协作有效,沟通及时到位,进度及时通报,测试风险及时化解……说明该项工作质量越高。 

  评价注意事项

  1、评价虽应尽可量客观,但无法避免主观因素。主观评价有时也是最直接、最简单的评价方式,因此,建议可以主观客观结合去评价,但应以客观评价为主。

  2、评价指标的收集,往往需要有效的统计数据来源,因此,要评价,还需要建立评价数据的收集机制。

  文章写的比较简略,如需讨论欢迎留言。

posted @ 2012-05-24 09:18 顺其自然EVO 阅读(290) | 评论 (0)编辑 收藏

QTP 录制 回放 原理

QTP 录制 回放 原理 
   自动化测试可以基于代码,例如junit,cppunit,可以基于协议,例如loadrunner,QTP是基于GUI界面的自动化测试工具。junit和cppunit属于单元测试,loadrunner和QTP属于系统测试。loadrunner用于系统的性能测试,QTP用于系统的功能测试。QTP是一款基于界面的功能自动化测试工具。
    LR录制的是协议和数据包,QTP录制的是鼠标和键盘的消息。QTP录制回放时基于windows操作系统的消息机制。QTP在录制时监听应用程序的消息,监听到之后把消息放到容器里,而另外的监听程序则从容器中取出容器中的消息,并调用对用的API处理函数。QTP截取的是用户对应用程序的操作,即录制的是消息。对于C/S应用程序,在回放时QTP根据对象的句柄(handle)和脚本内容,调用API函数;对于B/S应用程序,在回放时基于DOM(document object model)来解析。
    RO(Runtime Object)和TO(Test Object)。RO是指GUI界面上的控件即对象本身,TO是QTP对象库里的对象即在录制时添加到对象库的对象。RO是应用程序上实际的对象,因此拥有对象的所有的属性和方法;TO是QTP在录制时获取的应用程序的对象的,而录制时 QTP只保存了应用程序一部分常用的,能够在回放时识别应用程序上对象的一部分属性,因此TO对象拥有应用程序上对象的一部分属性。另外QTP对象库里保存的是TO对象。不过QTP可以使用Object Spy功能获取应用程序的所有属性值,或者可以通过TO对象的getROproperty()和getROpropertys()获取应用程序的所有属性值(呵呵,所有对象都没有setROproperty()方法)。使用TO对象的setTOproperty()和getTOproperty()可以操作对象库的TO对象。
    QTP的录制原理:根据用户在应用程序界面上的操作,QTP采用Object Identification Tools工具对被操作的对象进行识别,采用反编译的原理看其属于哪个插件类,从而进一步识别其属于什么控件类,然后QTP把对应的控件类实例化一个对象,并把获取的应用程序的一部分属性值(足以识别对象?)赋给新建的对象,并添加到对象库里即TO对象,而把用户对对象的操作添加到脚本里面。
    QTP的回放原理:QTP 根据脚本中记录下来的对象操作的顺序进行回放。QTP从脚本中读取到该对象,并根据对象的层次和名称到对象库中寻找相同名称的测试库对象,在测试库找到相应的对象,获得对象的属性,根据对象库中对象的属性,在运行的应用程序中进行匹配,寻找运行时对象,找到后根据脚本中记录的对该对象执行的动作和参数值。 

posted @ 2012-05-23 16:42 顺其自然EVO 阅读(835) | 评论 (0)编辑 收藏

在QTP中环境变量的使用

QTP使用环境变量

    为了在一次执行脚本过程中向数据库中插入多条数据,可以参数化,在Data Table中输入数值,但是现在我不想在Data Table中输入,而是通过VBS自动去生成。在这里声明的变量如何给下个ACTION使用呢?摸索了2天再参考网上的资料想出了一个解决方法,就拿目前测试的软件作为例子来讲解吧!

    想往数据库中插入多个产品(比如说10个),但又不想在Data Table中输入参数,想到一个下面的方法:分别录制一个登陆脚本和添加产品的脚本,然后在新建一空白test,call这两个脚本,其中登陆脚本只执行一次,而添加产品的脚本执行多次。在添加产品的脚本中对产品名称和产品编码参数化。产品名称暂时用“产品商标+空格+数字+字母”组成(形如:LG 221BWX),而产品编码就是产品名称去掉前面的商标跟空格。为了简单起见,只有数字这项作变动,其它的都不变。声明两个变量,用来拼组产品名称和产品编码。现在的问题是怎么将变量的值作为参数使得其在添加产品脚本中被使用。现在的脚本如下:

在QTP中环境变量的使用

在QTP中环境变量的使用

添加产品脚本一开始录制的脚本是这样的:

Browser("管理系统 | 主页").Page("管理系统 | 产品详细信息").WebEdit("productName").Set "LG 256BWX"
Browser("管理系统 | 主页").Page("管理系统 | 产品详细信息").WebEdit("productCode").Set "256BWX"

现在要做的就是把Dim productname,productcode 声明的两个变量的值传到这里供使用,为了实现这个目的,需做以下操作:

1.在File-Settings...中切换到Environment标签页,在Variable type下拉菜单中选择User_defined,如下图。

在QTP中环境变量的使用

然后点击“+”图标,会出现一个窗口,如下图,在这里我们只需填入Name即可。

在QTP中环境变量的使用

2.将声明变量的值传给环境变量,代码如下:

Environment("productname")=productname
Environment("productcode")=productcode

在QTP中环境变量的使用

3.修改添加产品脚本中以下代码

Browser("管理系统 | 主页").Page("管理系统 | 产品详细信息").WebEdit("productName").Set "LG 256BWX"
Browser("管理系统 | 主页").Page("管理系统 | 产品详细信息").WebEdit("productCode").Set "256BWX"

为:

Browser("管理系统 | 主页").Page("管理系统 | 产品详细信息").WebEdit("productName").Set Environment("productname")
Browser("管理系统 | 主页").Page("管理系统 | 产品详细信息").WebEdit("productCode").Set Environment("productcode")

4.保存,run脚本,测试通过

如何利用好qtp环境变量

qtp自动化测试过程中,经常会需要一些预先的配置信息文件来用于不同的测试环境下。环境变量也就这样应运而生了。在qtp中存在着两种类型的环境变量:

内置环境变量

介绍:它是qtp内部已经事先封装好.

方式:直接进入到Settings –> Environment中选择built-in选项即可查询到所有的内置环境变量。并且这些环境变量的值都是qtp已经封装好的。

举例: Msgbox Environment.Value(“TestName”)

结果:直接可以获取到当前测试脚本名称。

自定义内部环境变量

介绍:它是qtp中的一种可以在qtp内部进行自定义的环境变量

方式:直接进入到Settings –> Environment中选择User-defined选项,点击旁边的”+”,即可定义name 和 value

举例:Msgbox Environment.Value(“自定义名称”)

结果:直接可以获取到当前自定义环境变量的值

自定义外部环境变量

介绍:直接从xml格式文件中进行读取环境变量

方式:进入Settings –> Environment中选中Load variables and values from external file并给出xml的文件路径

举例:xml文件

<Environment>
<Variable>
<Name>Url</Name>
<Value>http://www.iquicktest.com</Value>
</Variable>
</Environment>

Msgbox Environment.Value(“Url”)

结果:直接打印结果为http://www.iquicktest.com

注意:环境变量中的属性名是大小写敏感的,必须要注意。

QTP使用环境变量


QTP使用环境变量的方法有两种,一种是自己设置环境变量,这种方法有很多朋友介绍过,不过我在使用的时候,更喜欢把它写在脚本中,这样不管脚本是移植到哪台机器上,环境变量都不用重新设置了,是不是方便很多,例如某个项目的主控脚本具体代码如下:

Dim Excel,Workbook,WorkSheet,run_value,scrīpt_name
Dim filepath,bnpFileName

Environment.Value("file_pash") = "C:\Documents and Settings\robel.yi\Desktop\BNP_scrīptS"

filepath=environment("file_pash")
bnpFileName=filepath & "\Data_file\Main_DATA.xlsx"

Set Excel= CreateObject("Excel.Application")
Set Workbook= Excel.Workbooks.Open(bnpFileName)
Set WorkSheet= workbook.Worksheets("Sheet1")

For i = 2 To 6
        run_value=CStr(WorkSheet.Cells(i, 3))
        If run_value = "Y" Then
                RunAction CStr(WorkSheet.Cells(i, 2))
        End If
Next

Excel.Quit
Set Workbook = nothing
Set WorkSheet = nothing

    这样,每次如果你把脚本移植到了另外一个地方,把新的路径赋值给环境变量file_pash就行了,其实也可以把它写在主控脚本的EXCEL驱动表里面,每次有变量在EXCEL里面修改就行了,对于把脚本给其他黑盒工程师来执行的时候,更加容易使用,其实有时候我们在写脚本的时候,一些小小的改动就能使自动化变得更智能方便,大家还有什么关于环境变量的使用的好的心得也不妨一起分享分享。

posted @ 2012-05-23 16:20 顺其自然EVO 阅读(3479) | 评论 (0)编辑 收藏

修改MYSQL最大连接数的3种方法

MYSQL数据库安装完成后,默认最大连接数是100,一般流量稍微大一点的论坛或网站这个连接数是远远不够的,增加默认MYSQL连接数的方法有两个

方法一:进入MYSQL安装目录 打开MYSQL配置文件 my.ini 或 my.cnf查找 max_connections=100   修改为 max_connections=1000 服务里重起MYSQL即可

方法二:MySQL的最大连接数默认是100客户端登录:mysql -uusername -ppassword

设置新的最大连接数为200:mysql> set GLOBAL max_connections=200

显示当前运行的Query:mysql> show processlist

显示当前状态:mysql> show status

退出客户端:mysql> exit

查看当前最大连接数:mysqladmin -uusername -ppassword variables

方法三:以centos 4.4 下面的mysql 5.0.33 手工编译版本为例说明:

  vi /usr/local/mysql/bin/mysqld_safe

  找到safe_mysqld编辑它,找到mysqld启动的那两行,在后面加上参数 :

  -O max_connections=1500

  具体一点就是下面的位置:

  用红字特别说明:

  then $NOHUP_NICENESS $ledir/$MYSQLD

  $defaults --basedir=$MY_BASEDIR_VERSION

  --datadir=$DATADIR $USER_OPTION

  --pid-file=$pid_file

  --skip-external-locking

  -O max_connections=1500

  >> $err_log 2>&1 else

  eval "$NOHUP_NICENESS $ledir/$MYSQLD

  $defaults --basedir=$MY_BASEDIR_VERSION

  --datadir=$DATADIR $USER_OPTION

  --pid-file=$pid_file

  --skip-external-locking $args

  -O max_connections=1500 >>

  $err_log 2>&1"

  保存。

  # service mysqld restart

  # /usr/local/mysql/bin/mysqladmin -uroot -p variables

  输入root数据库账号的密码后可看到

  max_connections 1500 即新改动已经生效。

  还有一种方法,

  修改原代码:

  解开MySQL的原代码,进入里面的sql目录修改mysqld.cc找到下面一行:

  {"max_connections", OPT_MAX_CONNECTIONS,

  "The number of simultaneous clients allowed.", (gptr*) &max_connections,

  (gptr*) &max_connections, 0, GET_ULONG, REQUIRED_ARG, 100, 1, 16384, 0, 1,

  0},

  把它改为:

  {"max_connections", OPT_MAX_CONNECTIONS,

  "The number of simultaneous clients allowed.", (gptr*) &max_connections,

  (gptr*) &max_connections, 0, GET_ULONG, REQUIRED_ARG, 1500, 1, 16384, 0, 1,

  0},

  存盘退出,然后./configure ;make;make install可以获得同样的效果。

posted @ 2012-05-23 11:16 顺其自然EVO 阅读(430) | 评论 (0)编辑 收藏

tomcat服务器连接池知识

tomcat服务器连接池知识  

什么是敏捷开发

简单的说,敏捷开发是一种以人为核心、迭代、循序渐进的开发方法。在敏捷开发中,软件项目的构建被切分成多个子项目,
各个子项目的成果都经过测试,具备集成和可运行的特征。换言之,就是把一个大项目分为多个相互联系,但也可独立运行的小项目,
并分别完成,在此过程中软件一直处于可使用状态。

Tomcat中配置连接池步骤

本方法的原理是,在%CATALINA%\conf\server.xml中设置数据库的连接属性,
在应用目录的/WEB-INF/web.xml中配置一个引用,
然后在应用中的/META-INF/context.xml中将以上两个配置联系起来。
所以真正产生连接的是tomcat系统级,因此数据库驱动应该放在%CATALINA%\common\lib中

以下是具体步骤:

1.将数据库驱动程序的JAR文件放在Tomcat的 common/lib 中。

2.在server.xml中设置数据源,以MySQL数据库为例,如下:
在<GlobalNamingResources> </GlobalNamingResources>节点中加入,
  <Resource
  name="jdbc/DBPool"
  type="javax.sql.DataSource"
  password="root"
  driverClassName="com.mysql.jdbc.Driver"
  maxIdle="2"
  maxWait="5000"
  username="root"
  url="jdbc:mysql://127.0.0.1:3306/test"
  maxActive="4"/>
  属性说明:name,数据源名称,通常取”jdbc/XXX”的格式;
  type,”javax.sql.DataSource”;
  password,数据库用户密码;
  driveClassName,数据库驱动;
  maxIdle,最大空闲数,数据库连接的最大空闲时间。超过空闲时间,数据库连
  接将被标记为不可用,然后被释放。设为0表示无限制。
  MaxActive,连接池的最大数据库连接数。设为0表示无限制。
  maxWait ,最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示
  无限制。

3.在你的web应用程序的web.xml中设置数据源参考,如下:
  在<web-app></web-app>节点中加入,
  <resource-ref>
  <description>MySQL DB Connection Pool</description>
  <res-ref-name>jdbc/DBPool</res-ref-name>
  <res-type>javax.sql.DataSource</res-type>
  <res-auth>Container</res-auth>
  <res-sharing-scope>Shareable</res-sharing-scope>
 </resource-ref>
  子节点说明: description,描述信息;
  res-ref-name,参考数据源名字,同上一步的属性name;
  res-type,资源类型,”javax.sql.DataSource”;
  res-auth,”Container”;
  res-sharing-scope,”Shareable”;

4.在web应用程序的/META-INF/context.xml中设置数据源链接,如下:
  在<Context></Context>中加入:
  <Context>
  <ResourceLink
  name="jdbc/DBPool" 
  type="javax.sql.DataSource" 
  global="jdbc/DBPool"/>
  </Context>
  属性说明:name,同第2步和第3步的属性name值,和子节点res-ref-name值;
  type,同样取”javax.sql.DataSource”;
  global,同name值。
 
至此,设置完成,下面是如何使用数据库连接池。
1.建立一个连接池类,DBPool.java,用来创建连接池,代码如下:
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class DBPool {
  private static DataSource pool;
  static {
  Context env = null;
  try {
  env = (Context) new InitialContext().lookup("java:comp/env");
  pool = (DataSource)env.lookup("jdbc/DBPool");
  if(pool==null) 
  System.err.println("'DBPool' is an unknown DataSource");
  } catch(NamingException ne) {
  ne.printStackTrace();
  }
  }
  public static DataSource getPool() {
  return pool;
  }
}

2.在要用到数据库操作的类或jsp页面中,用DBPool.getPool().getConnection(),
获得一个Connection对象,就可以进行数据库操作,最后别忘了对Connection对象调用close()方法,
注意:这里不会关闭这个Connection,而是将这个Connection放回数据库连接池。

数据源和数据库共享连接池关系

一、数据源简介:
在Java语言中,DataSource对象就是一个代表数据源实体的对象。一个数据源就是一个用来存储数据的工具,它可以是复杂的大型企业级数据库,
也可以是简单得只有行和列的文件。数据源可以位于在服务器端,也可以位于客服端。

应用程序通过一个连接来访问数据源,那么一个DataSource对象就是用于提供连接数据源的工具。DataSource接口提供了两个方法用于建立和数据源的连接,
使用DataSource对象建立和数据库的连接比起使用DriverManager接口更加高效,虽然两者的使用范围都很相似,并且都提供了方法用于建立和数据库的连接,设置连接的最大超时时间,获取流、登录。
  
但两者之间的区别更加明显。和DriverManager不同,一个DataSource对象能够识别和描述它所代表的数据源的属性,
而且 DataSource对象的工作和JNDI(Javatm Naming and Directory Interfaceti)具有密切的关系, DataSource的建立、发布、独立于应用程序的管理都依靠JNDI技术。
   
在JDBC2.0或JDBC3.0中,所有的数据库驱动程序提供商必须提供一个实现了DataSource接口的类,要使用数据源必须首先在JNDI中注册该数据源对象。
如果在JNDI中注册了数据源对象,将会比起使用DriverManager来具有两个方面的优势:
首先程序不需要像使用DriverManager一样对加载的数据库驱动程序信息进行硬编码,程序员可以选择先在JNDI中注册这个数据源对象,然后在程序中使用一个逻辑名称来引用它,
JNDI会自动根据你给出的名称找到与这个名称绑定的DataSource对象。然后就可以使用这个DataSource对象来建立和具体数据库的连接了。

其次,使用实现了DataSource接口的类所具有的第二个优势体现在连接池和分布式事务上。连接池通过对连接的复用而不是新建一个物理连接来显著地提高程序的效率。
从而适用于任务繁忙、负担繁重的企业级分布式事务。

二、JNDI简介:
JNDI是用于向Java程序提供目录和命名功能的API。它被设计成独立于特定的目录服务,所以各种各样的目录都可以通过相同的方式进行访问。

可以简单地把JNDI理解为一种将对象和名字绑定的技术,对象工厂负责生产出对象,这些对象都和惟一的名字绑定。外部程序可以通过名字来获取对某个对象的引用。

在Intranets (企业内部网)和Internates(互联网)中目录服务(Directory service)扮演了一个非常重要的角色,它能够在众多的用户、机器、网络、服务、应用程序中访问各种各样的信息。
目录服务提供了一系列的命名措施,用人类可以理解的命名方式来刻画各种各样的实体之间的关系

一个企业式计算环境(computing environment)通常是由若干代表不同部分的命名复合而成。比如在一个企业级环境中DNS (Domain Name System)通常被当成顶层的命名方案(top-level namein facility)区分不同的部门或组织。而这些部门或组织自己又可以使用诸如LADP或NDS的目录服务

从用户的角度来看,这些都是由不同的命名方案构成的复合名称。URL就是一个很典型的例子,它由多个命名方案构成。使用目录服务的应用程序必须支持这种复合构成方式

使用目录服务API的Java开发人员获得的好处不仅在于API独立于特定的目录或命名服务,而且可以通过多层的命名方案无缝访问 (seamless acess)目录对象。实际上,
任何的应用程序都可以将自身的对象和特定的命名绑定起来,这种功能可以使到任何的Java程序查找和获取任何类型的对象

终端用户可以方便地使用逻辑名称从而轻易地在网络上查找和识别各种不同的对象,目录服务的开发人员可以使用API方便地在不同的客服端之间切换而不需要作任何更改


三、数据源和连接池的关系:
JDBC2.0提供了javax.sql.DataSource接口,它负责建立与数据库的连接,在应用程序访问数据库时不需要编写连接数据库的代码,可以直接从数据源获得数据库连接。
 
在DataSource中事先建立了多个数据库连接,这些数据库连接保存在连接池(Connect Pool)中。Java程序访问数据库时,只需要从连接池中取出空闲状态的数据库连接;
当程序访问数据库结束,再将数据库连接放回连接池。


四、数据源和JNDI的关系:
DataSource对象是由Tomcat提供的,因此不能在程序中采用创建一个实例的方式来生产DataSource对象,而需要采用Java的另一个技术JNDI,来获得DataSource对象的引用。

Tomcat把DataSource作为一种可以配置的JNDI资源来处理。生成DataSource对象的工厂为org.apache.commons.dbcp.BasicDataSourceFactory。

在javax.naming包中提供了Context接口,该接口提供了将对象和名字绑定,以及通过名字检索对象的方法。Context中的主要方法有:
bind(String name,Object object):将对象与一个名字绑定
lookup(String name):返回与指定的名字绑定的对象


五、Tomcat中数据源的配置:
数据源的配置涉及修改server.xml和web.xml,在server.xml中加入定义数据源的元素<Resource>,在web.xml加入<resource-ref>元素,声明该Web应用所引用的数据


A.在server.xml中加入<Resource>元素:<Resource>元素用来定义JNDI Resource。
  
属性    描述
name    指定Resource的JNDI名字
auth    指定管理Resource的Manager,它有两个可选值:Container、Application
type    指定Resource所属的Java类名

<Resource name = "jdbc/BookDb" 
auth = "Container" 
        type = "javax.sql.DataSource" />

B.在<Resource>元素中加入<ResourceParams>元素:<ResourceParams>元素用来指定各种参数值
  
属性    描述
factory    指定生成的DataResource的factory类名
maxActive    指定数据库连接池中处于活动状态的最大连接数目,0表示不受限制
maxIdle    指定数据库连接池中处于空闲状态的最大连接数目,0表示不受限制
maxWait    指定连接池中连接处于空闲状态的最长时间,超过会抛出异常,-1表示无限
username    指定连接数据库的用户名
password    指定连接数据库的口令
driverClassName    指定连接数据库的JDBC驱动程序
url             指定连接数据库的URL

   <ResourceParams name = "jdbc/BookDb">

    <parameter>
       <name>factory</name>
       <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
    </parameter>

    <parameter>
       <name>maxActive</name>
       <value>100</value>
    </parameter>

    <parameter>
       <name>maxIdle</name>
       <value>30</value>
    </parameter>

    <parameter>
       <name>maxWait</name>
       <value>10000</value>
    </parameter>

    <parameter>
       <name>username</name>
       <value>user</value>
    </parameter>

    <parameter>
       <name>password</name>
       <value>1234</value>
    </parameter> 

    <parameter>
       <name>driverClassName</name>
       <value>com.mysql.jdbc.Driver</value>
    </parameter>

    <parameter>
       <name>url</name>
       <value>jdbc:mysql//localhost:3306/BookDb?autoReconnect=true</value>
    </parameter> 

</ResourceParams>

C.在web.xml中加入<resource-ref>元素:<resource-ref>元素表示在Web应用中引用JNDI资源
  
属性                  描述
description    对所引用的资源的说明
res-ref-name    指定所引用资源的JNDI名字,与<Resource>元素中的name属性对应
res-type             指定所引用资源的类名字,与<Resource>元素中的type属性对应
res-auth             指定所引用资源的Manager,与<Resource>元素中的auth属性对应

 

六、在Web应用中使用数据源:
javax.naming.Context提供了查找JNDI Resource的接口,可以通过三个步骤来使用数据源对象:

A.获得对数据源的引用:
    Context ctx = new InitalContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/BookDb");

    B.获得数据库连接对象:
        Connection con = ds.getConnection();
  
    C.返回数据库连接到连接池:
        con.close();

 

在连接池中使用close()方法和在非连接池中使用close()方法的区别是:前者仅仅是把数据库连接对象返回到数据库连接池中,
是连接对象又恢复到空闲状态,而非关闭数据库连接,而后者将直接关闭和数据库的连接


七、发布使用数据源的Web应用:
  如果直接同JDBC访问数据库,可以把JDBC驱动程序拷贝到Web应用的WEB-INF/lib目录或者Tomcat安装目录下的common/lib目录下。
  
  如果通过数据源访问数据库,由于数据源由Servlet容器创建并维护,所以必须把JDBC驱动程序拷贝到Tomcat安装目录下的common/lib目录下,确保Servlet容器能够访问驱动程序。


数据库连接池的主要操作如下:
  
  (1)建立数据库连接池对象(服务器启动)。
  
  (2)按照事先指定的参数创建初始数量的数据库连接(即:空闲连接数)。
  
  (3)对于一个数据库访问请求,直接从连接池中得到一个连接。假如数据库连接池对象中没有空闲的连接,且连接数没有达到最大(即:最大活跃连接数),创建一个新的数据库连接。
  
  (4)存取数据库。
  
  (5)关闭数据库,释放所有数据库连接(此时的关闭数据库连接,并非真正关闭,而是将其放入空闲队列中。如实际空闲连接数大于初始空闲连接数则释放连接)。
  
  (6)释放数据库连接池对象(服务器停止、维护期间,释放数据库连接池对象,并释放所有连接)。

posted @ 2012-05-23 10:59 顺其自然EVO 阅读(661) | 评论 (0)编辑 收藏

软件测试Bug之优先级

 Bug的优先级是bug管理过程中必须考虑的问题。对于优先级的划分,不同的软件公司有自身的一套制度,因此笔者介绍的也仅仅是自己比较喜欢的一种方式。

  为了便于bug的提交和管理,也为了方便于与开发人员进行交流,笔者倾向于在项目中将bug划分为三个等级,而不是网络上流传的五等级版本甚至七等级版本(在成熟的软件组织中,使用更细的bug等级划分有利于bug分类,质量评审等工作的顺利进行)。这三个优先级分别即优先级1(严重的),优先级2(比较严重的)和优先级3(一般的)。

  笔者划分bug等级的指导思想很简单,严重影响测试执行的bug是最严重的,即优先级1 的bug,除此之外所有导致应用程序崩溃掉的bug也列入到优先级1中;其他功能性bug列入比较严重的bug的队伍,即优先级2;界面上的bug列为一般的,即优先级3。

  作者在实践过程中推行的就是这种bug分级制度。这种分级制度看起来过于主观,而不像网络上流传的各个bug优先级划分的版本中,将每个优先级的bug的表现都一五一十列出来,如下是笔者以前使用到一个bug优先级划分文档中列出的优先级1的bug特征:

  a)应用程序某个模块功能未实现(包括整个模块不能运行)

  b)用户的信息被破坏或者丢失

  c)可重现的不可避免的崩溃,死锁

  d)功能和性能急剧衰退

  e)严重的内存泄漏

  f)导致功能无法正常使用的UI设计(UI响应迟缓)

  g)其他

  的确,这些bug优先级划分很明确,让人一目了然并且觉得很有道理,可是拿到实际中一用,麻烦开始来了。因为某些描述仍然不够详细,含混不清的描述诸如“功能和性能急剧衰退”,碰到这种描述,不同的人会有不同的理解,而不同的理解必然会带来各种各样的问题。因此,笔者在实践中逐渐摒弃了这种做法(当然,笔者并不排除将来可能还是会回到一些比较正规的管理方法上来,但是目前这种“标准”方法并不适合笔者所在的公司~),并开始逐步推广笔者自己刚才提到的粗放式bug优先级划分方法。

  对于该划分方法,笔者还需要进一步的说明。笔者刚才提到的“严重影响测试执行的bug”其实也是指系统的基本功能或者核心功能,比如新建编辑删除功能中,对于同样是信息为保存到数据库——即新建后记录未添加到数据库,编辑后记录未更新,删除后数据仍然存在于数据库中——这时候笔者仅仅将新建功能的该bug置于优先级1中,编辑删除bug则置于优先级2中。这种方法与很多正统的方法很不一致,因为在很多划分方法中“信息未保存”都是优先级1的bug。但是笔者自认为这样做是有理由的:当新建功能发生该类型bug而编辑删除功能正常时,编辑删除功能仍然无法测试或者实现(因为没有数据啊),这在客户的江渡看来会直接视为新建编辑删除功能均未实现。新建功能正常而编辑或者删除功能失效,则不会影响到其他功能的使用(当仅编辑功能失效的时候,新建和删除功能并不会受到影响),测试人员仍然进行新建删除功能的功能测试,客户依然可以使用新建和删除功能。

  当然,笔者使用上面的划分方式还有其他的原因——基于bug管理和测试开发工作的顺利推进。读者可能会注意到,使用上面的bug划分方式会减少优先级1的bug的数量,笔者这样做是因为笔者在bug管理中推介的方式是优先级1的bug不允许推迟到下一个工作日修改。试想,如果优先级1的bug的数量如果过多自然会导致这种管理方式推行的极大阻力——没有哪个开发人员会喜欢让自己一整天的时间花费在修复bug上。当我们提交的优先级1的bug都是非常紧急的,会影响到开发或者测试的进度的话,开发人员就自知理亏不得不去修复这些bug了,这就保证了即使到了项目很急迫的时间,我们项目的主体功能还是稳定可用的,并有效遏制了严重bug的生存期。

  对于优先级2与优先级3 的划分点,只是笔者个人看法,因为笔者目前所经历的项目都是功能性为主,因此对于UI相关的要求相对较低,因此笔者采取了这种粗放的方式(将UI相关bug归纳为优先级3,其他的非UI的非优先级1的bug全部塞到优先级2 的集装箱中~)。

  PS:感谢下面一位同仁的回复,优先级1类的bug还应该包括功能严重不符合产品说明书这种类型的bug。

  以上为个人意见,如有意见建议,欢迎一起交流。

posted @ 2012-05-23 10:07 顺其自然EVO 阅读(977) | 评论 (0)编辑 收藏

Linux中的两种文件锁——协同锁与强制锁

 Linux文件锁的示例

  为了理解文件锁是如何工作的,我们建立程序文件file_lock.c:

#include <stdio.h>
#include <fcntl.h>
  
int main(int argc, char **argv) {
  if (argc > 1) {
    int fd = open(argv[1], O_WRONLY);
    if(fd == -1) {
      printf("Unable to open the file\n");
      exit(1);
    }
    static struct flock lock;
  
    lock.l_type = F_WRLCK;
    lock.l_start = 0;
    lock.l_whence = SEEK_SET;
    lock.l_len = 0;
    lock.l_pid = getpid();
  
    int ret = fcntl(fd, F_SETLKW, &lock);
    printf("Return value of fcntl:%d\n",ret);
    if(ret==0) {
      while (1) {
        scanf("%c", NULL);
      }
    }
  }
}

  用gcc编译此程序:

# cc -o file_lock file_lock.c

  使用mount命令带“mand”参数来重新挂载根文件系统,如下所示。这将在文件系统级别使能强制锁功能。

  注意:你必须切换到root用户才能执行下面的命令。

# mount -oremount,mand /

  在可执行的(file_lock所在的)目录中创建两个名为“advisory.txt”和“mandatory.txt”的文件。对于“mandatory.txt”使能Set-Group-ID,同时不使能Group-Execute-Bit,如下所示:

# touch advisory.txt
# touch mandatory.txt
# chmod g+s,g-x mandatory.txt

  测试协同锁:执行示例程序,以“advisory.txt”作为参数。

# ./file_lock advisory.txt

  此程序将等待用户的输入。从另一个终端或控制台,尝试输入以下命令行:

# ls >>advisory.txt

  在上面的例子中,ls命令会将其输出写入到advisory.txt文件中。即使我们获得了一个写入锁,仍然会有一些进程(非合作)能够往文件里写入数据。这就是所谓的“协同”锁。

  测试强制锁:再次执行示例程序,以“mandatory.txt”作为参数。

# ./file_lock mandatory.txt

  从另一个终端或控制台,尝试输入以下命令行:

# ls >>mandatory.txt

  在上面的例子中,ls命令在将其输出写入到mandatory.txt文件之前,会等待文件锁被删除。虽然它仍然是一个非合作进程,但强制锁起了作用。

 文件锁是一种文件读写机制,在任何特定的时间只允许一个进程访问一个文件。利用这种机制能够使读写单个文件的过程变得更安全。

  在这篇文章中,我们将探讨Linux中不同类型的文件锁,并通过示例程序来理解它们之间的不同之处。

  我们将采取以下的例子来解释为什么需要使用文件锁。

  1、进程“A”打开和读取一个文件,此文件包含账户相关的一些信息。

  2、进程“B”也打开了这个文件,并读取了文件中的信息。

  3、现在,进程“A”更改了其副本中的一条余额记录,并将其写入文件。

  4、此时,进程“B”并不知道上次读取的文件已经被更改,它还保存着原始的文件副本。然后,进程“B”更改了“A”操作的那条相同的记录,并将记录写入文件。

  5、此时,文件中将只保存了进程“B”更改过的记录。

  为了避免这种事情发生,就要使用文件锁来确保操作的“序列化”。

  以下是Linux系统中两种常用的文件锁:

  1、协同锁

  2、强制锁

  1、协同锁

  协同锁要求参与操作的进程之间协同合作。假设进程“A”获得一个WRITE锁,并开始向文件中写入内容;此时,进程“B”并没有试图获取一个锁,它仍然可以打开文件并向文件中写入内容。在此过程中,进程“B”就是一个非合作进程。如果进程“B”试图获取一个锁,那么整个过程就是一个合作的过程,从而可以保证操作的“序列化”。

  只有当参与操作的进程是协同合作的时候,协同锁才能发挥作用。协同锁有时也被称为“非强制”锁。

  2、强制锁

  强制锁不需要参与操作的进程之间保持协同合作。它利用内核来查检每个打开、读取、写入操作,从而保证在调用这些操作时不违反文件上的锁规则。关于强制锁的更多信息,可以在kernal.org上找到。

  为了使能Linux中的强制锁功能,你需要在文件系统级别上打开它,同时在单个文件上打开它。其步骤是:

  1、挂载文件系统时使用“-omand”参数。

  2、对于要打开强制锁功能的文件lock_file,必须打开set-group-ID位,关闭group-execute位。(选择此方法的原因是,当你关闭group-execute时,设置set-group-ID就没有实际的意义了)

posted @ 2012-05-23 09:32 顺其自然EVO 阅读(4101) | 评论 (0)编辑 收藏

鸟也能飞:SQL数据库实战专业教程(一)

写在前面的话:这个教程本来是想重点介绍SQL数据库,但是写着写着发现已经有很多关于数据的知识了,索性把项目整个开发过程都写了下来。由于时间确实很紧,教程收尾比较匆忙,感觉写的很对不起大家,但是基本上一个项目的流程就这么多了(不包括后期测试),什么时候该做什么,该用什么工具,有什么技巧,都差不多说到了,我的能力也就到这了。补充一点:每个阶段都要产生相应的文档,这可是非常重要的,真正做项目的时候不要忽略了!

  直到现在,接触数据库的时间大约有一年了,刚开始用的时候真是一点也不懂,就那么硬生生的用,经过一年的积累,虽然还算是小菜,但是也懂得点东西。为了整理知识,同时帮助更多的人能更好的学习数据库,我决定把我学到的东西整理下来。鉴于网络上有很多关于数据库的教程,但很多都是孤立的知识点,这样初学者很难从整体上把握数据库,学习起来比较困难,也会产生一定的畏惧心理;同时,面对网络上繁多的资料,初学者往往很难正确选择比较的好的方法,很可能掌握的是过时或者不太实用的技术,当然包括我在内也是这样。

  基于以上情况,本教程基于数据库实际应用,展示一个完整的数据库项目开发过程,其中会涉及一些数据库设计的经验技巧、项目开发过程中一些工具的使用、三层架构等。当然,本人也是小菜,再加上时间有限,不可能把所有的知识点都说到,一些基础的知识一带而过,重在宏观指导,重在应用体验,细节读者自己去百度或google。教程的目的是引导初学者成为一名专业的数据库应用开发者。本教程以SQL2005数据库和C#为例,构建一个简单的学生管理系统。本教程虽然为初级教程,但也不适合0基础的童鞋,适合处于迷茫状态的童鞋。废话少说,进入正题。

  一、为什么要选择SQL数据库。

  作为国际惯例,要先说一下为什么要选择SQL数据库。其实也不是SQL数据库有多好,比SQL优秀的数据库俺这种小菜就知道好几个,主要原因是SQL数据相对于其他数据库较为简单,包括安装、配置、使用,数据库原理都是相同的,把这个学好了其他的也不成问题;另一方面SQL数据库已经很专业了,学习的性价比高,总比学习Access数据库要好吧?其实,俺也只懂SQL而已。至于SQL数据库具体是什么、有什么特点,这个就留给读者去寻找答案了,相信地球人都能找到。

  二、数据库原理简介。

  数据库,顾名思义,就是存放数据的地方。SQL server 2005就是一个大型的数据库管理系统,在这个大型系统中我们可以建立很多数据库,用来为不同的程序服务,一般一个应用程序只有一个数据库。有了数据库就涉及数据具体内容,SQL数据库中包含了N个表,每个表都包括行和列,其中每个列叫做一个字段,每一行叫做一条记录,就好像坐标一样,x轴是记录,y轴是字段。字段是创建表时就定义好的,它有名称和类型,这些字段连在一起就是一条记录,所以理论上表可以有无限个记录。一个数据库中的N个表,通过关系联系在一起,就是传说中的关系型数据库。好像有些乱,下面来个图:

  这些都是最基本的知识,为什么要提这些?说实话,我当初是费了好大劲才理解这个的,在这提出来,就是想给读者一些点播。

三、权限配置。

  其实权限配置你完全不懂,也可以正常使用数据库,一般都是直接使用sa账户。作为一个完整的数据库教程,并且号称是专业的教程,必须涉及权限配置,可以不懂,但必须了解。

  SQL server作为一个数据库平台,肯定不是给一个人用的,既然多人使用,就要涉及权限问题。比如A用户不能随意更改B用户的数据库、作为用户的A不能有系统管理员的权限等。说道权限就不得不提角色,什么是角色呢?简单的讲,就是某些权限的集合,也可以说是一组权限。这样一来,想让某个用户具有某类功能,只要指定角色就可以了,而不用一个一个的去分配权限,这样使权限分配变得更加快捷方便,并且有统一性。

  SQL server中的角色图:

  需要注意的是,服务器角色是在整个SQL平台层面上的角色,它只有固定角色(系统定义好的权限组);数据库角色是数据库层次上的角色,一个用户是A数据库的db_owner角色,不一定是B数据库的db_owner角色,它分为固定角色和自定义角色。

  学习这些现在可能用不到,但是权限管理作为数据库一个重要特色,在将来的工作中一定有用武之地。现在看不懂也没事,了解即可。在这只列举了角色类型,并没有给出创建方法,google吧!

posted @ 2012-05-23 09:31 顺其自然EVO 阅读(212) | 评论 (0)编辑 收藏

程序员的7个坏习惯

  1、所有的代码都是垃圾,除了我的以外

  哥们儿,我有一个坏消息要告诉你,你的代码很垃圾。不管你花费了多少精力,总是有很多程序员认为你的代码是垃圾,他们可以比你做的10倍更好。

  怎么解决:不要只顾着批评,你也是写代码的人。尝试去客观的评价,而不是审判!谦虚一点。

  2、“马上搞定”带来的灾难

  大家都喜欢走捷径,每个人都做过。有时候走捷径是必须的,但是大部分情况下走捷径是危险的,非常危险,所以我们应该尽量避免。一个捷径可能省了你几个小时的时间,但是可能为你带来几个月的痛苦。

  怎么解决:不要一个人偷偷的把事情搞定,而是和别人商量,请别人检查你的工作。假如你要采取捷径的方法,请必须经过所有相关人员,包括你的经理的同意。

  3、错误的估算时间

  我住在巴塞罗那,我们那里有非常有名的 Sagrada Familia 教堂。它出名的原因是因为它非常精美,并且预计的建造时间非常长,从1882年开始。我想这个项目在做时间预算的时候没有程序员参与,否则建造完成时间可能是两个礼拜。

  怎么解决:首先,准确的预估软件开发的时间是不可能的,我们只能猜。在开发的过程中你可能遇到各种你没有遇见的问题,所以我们往往在预估的时间上乘以2。

  4、盲目自大

  程序员之间的讨论有时候看起来像是公鸡打架而不是人类谈话。这往往发生在设计和架构的会议上。在你的眼里,其他人的意见都是扯蛋,扯蛋,扯蛋……

  怎么解决:把自大留在家里,冷静的听听别人在说什么,再做决定。

  5、“这不是我干的!”

  我们总是给自己找借口。我们往往给自己开脱,比如在正常情况下,我永远也不会犯那样的错误云云。

  怎么解决:谁都会犯错误,调整你的态度,“是的,这是我的问题,我们现在需要修复它”就是一种非常专业的态度。它会帮助你建立你的声望,并获得同事的好感。

  6、缺乏耐心

  虽然重复的简单工作很枯燥,但是那也是必须完成的任务。程序员往往抵制,反感这些任务,导致工作效率低下。

  怎么解决:纪律!记住,再激动人心的项目也有枯燥无味的任务要完成!

  7、不成熟

  如果编程是做爱的话,一定有很多没有被满足的电脑。你不能简单的进入,做到一半然后就睡觉了。很多程序员都没有搞明白“完成”是什么概念。请记住:完成意味着测试通过(不仅仅是单元测试),文档完整,提交,合并……

  怎么解决:这一条很难,很多看起来不必要的任务加在一起很复杂。但这是完成一个项目所必须的工作,需要通过纪律和训练来完成。

posted @ 2012-05-23 09:25 顺其自然EVO 阅读(192) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 324 325 326 327 328 329 330 331 332 下一页 Last 
<2025年7月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜