产品经理的职责是探索产品的价值与挖掘需求并分析可用性、可行性,然后联合交互设计与技术开发人员,共同定义产品的特性并将他实现。在此过程中,产品经理作为中间枢纽,需要和团队成员进行沟通合作,驱动团队执行力与提升
工作效率。那么如何成为好的产品经理呢???
以前我说过:“只要肯用心,每个人都可以成为产品经理!做一名成功的产品经理很容易,只是做一名成功产品的产品经理不容易! ”因此,在成功之前我们先要成为一名出色的产品经理。
下面我将以步骤拆分,一步一步讲讲如果成为出色的产品经理。
一、想成为产品经理
首先我们通过某个途经了解到产品经理这个职位,当我们深入了解后开始坚定自己要成为一名产品经理。此时我们就需要了解成为一名产品经理需要哪些准备。下面我就介绍介绍成为产品经理的准备,这些准备大多来自于企业对应聘岗位的要求,但是我们自己也需要对自身有所要求。
1、人品
人品这个词是套用了网络词语,真正含意是个人素质和态度。产品经理在公司是一个处在中间位置的圆滑角色,因此产品经理的素质和态度非常重要。经验不足可以积累,知识不全可以
学习,但是素质和态度是长期形成的人性习惯,很难改变,因此企业招聘对于这一条非常重视,我们自己也要时刻反省并提升自己。
2、兴趣
正如我开头所说的,作为一名产品经理必须要热爱这份职业,因此我们必须要对产品有足够的兴趣。无论是产品经理还是其他职业都一样,将自己的兴趣转化成职业是最佳的选择,当我们有兴趣并热爱这份职业时,才能调动出最大的激情,也能在工作中享受到这份喜悦。
在这里我说一句题外话:每一个团队都有或多或少的毛病,竟然你选择了这份职业,选择了这个团队,那么请不要轻易放弃。
3、产品思维
不知道你有没有关注过产品经理的微博,在他们的微博里,你总能看到他们在
生活中对各种各样的产品提出各种各样的建议。在产品人的眼里,一切事物都是产品;产品人的思维总是在不自觉的情况下挑刺,这并非指产品人都是另类洁癖,而是产品人的思维总是以用户体验的角色对产品提出体验上的缺陷。产品思维就是一种换位思考的思维,以用户的视角,融入到产品中并挑出产品设计的缺陷。这是一个可以积累的思维方式,但是在成为产品经理之前我们需要有这方面的意识。
4、逻辑思维
这是一个非常重要的必备条件,产品经理的工作不仅仅是勤奋就足够的,在产品设计中,有很多细节问题包含各式各样的逻辑问题,我们需要理清之间的关联,写出或者画出可行的产品执行方案。我们还需要具有逻辑分拆、组合、混合的思维能力,在产品人的思维里,任何问题都没有唯一的答案,一项功能总能有多种实现方案,我们需要清晰的理解并选出最佳的方案。
5、信心
有一种信心叫自信,而这种自信的解释就是:“眼睛尚未看见就相信,其最终的回报就是你真正看见了。”我以前在“PM面试中需要注意的事项”
文章中有说过,产品经理是一个务实的职业,千万不要在团队成员面前浮夸产品,我们需要寄于数据说话(虽然很多时候数据不一定准确,但是产品经理的言论需要有依据。)。
这里我所指的信心是来自内心的自信,我们需要相信自己能够做好产品经理,并且从内心散发出自信的气息,感染团队里每一个人,从而调动大家的积极性。
二、成为产品经理之前
当我们做好成为产品经理的准备的时候,下一步我们就需要知道产品经理的工作性质,而这些性质是我们对自身的一种要求,更多的动力来自于我们对这份职业的热爱。
1、工作时间
产品经理的工作绝对不是朝九晚五类型的,一位出色的产品经理也绝对不是朝九晚五的工作,这也就是我前面说到的,我们需要对这份职业有浓厚的兴趣,只有当我们有兴趣和热爱这份职业才能有动力做好产品经理。产品就像产品经理的孩子,产品经理需要对这个孩子的前途和命运负责,因此产品经理的工作无法用时间来衡量,在工作之余我们也无法将产品抛诸脑后。
2、工作态度
以职业划分,产品经理是一个光杆司令,没有直接的职权管理团队成员,更不能要求团队成员执行命令。产品经理的工作职责是与多部门沟通合作,通过说服力促使大家通力合作,因此良好的态度有助于提升团队工作效率,亲和的沟通方式有助于保证工作的质量。
3、掌握技术
虽然产品经理不是直接操作技术的人,但是至少需要了解技术的实现原理,这里所指的技术不仅仅是程序,也包括交互设计。有部分企业会看重产品经理对技术的了解,但是很多企业相信这不重要,因为技术原理的学习很快,所以在入职前这不是必备的条件。
三、成为产品经理
当我们正式成为一名产品经理的时候,并不代表我们是一名出色的产品经理,下面我再说说产品经理在工作当中需要具备的能力。
1、善于挖掘性的思考
发现一个点,先逻辑分类,再逻辑串联,最后再数据挖掘,突现出产品价值,这就是产品的孵化过程! 产品经理的职责就是探索产品的价值与挖掘需求并分析可用性、可行性。
2、需求管理
在工作中我们会不断的收到需求,这些需求有来自领导、团队成员、用户、自己,我们需要对这些需求进行整合并分析可用性、可行性,在确定可行的情况下还需要进一步规划他的实施,而实施之前我们需要通过文档或者原型做用例推演,推演得出逻辑可行,下一步再高保真的效果演示,最终才能实施。工作中最忌缺失需求管理,不能收到需求直接给技术人员实施,也不能经常变更需求。
3、理解用户体验设计
有些企业会有独门负责用户体验研究的人员,这些人员被称为用户研究,他们与产品经理、交互设计、视觉设计共同完成产品原型设计和高保真模型设计。也有企业没有这样的人员,而此时通常需要产品经理自己理解用户体验设计,与设计师共同完成原型设计。
4、时间管理
无论什么职业,时间管理尤其重要,时间管理也叫日程管理,是我们日常工作安排的管理,合理的时间管理可以帮我们明确轻重缓急,提升工作效率。下面四种等级是很多企业常用的工作优先级的排序规则。
重要(紧急)
重要(不紧急)
不重要(紧急)
不重要(不紧急)
5、化解压力与矛盾
产品经理是团队的枢纽点,也是压力、矛盾的集中营,所以我们自己需要有一个良性的化解压力的方法,在解决自身压力的同时还要化解团队的矛盾。
解压的方法每个人都有不同,但是化解矛盾都是大同小异的。首先我们需要先知道矛盾往往先是从抱怨与指责开始的,我们在处理抱怨与指责的时候需要清楚,这是一个毒瘤,会越长越大,最终会直接影响团队的和谐,所以我们必须要制止它的发展,避免大家陷入这样的惯性旋窝中。
抱怨与指责是双向的,别人可以迷失,但是我们作为产品经理必须要保持理性,结束抱怨与指责,主动道歉,终止抱怨与指责不断的扩大,那怕不是我们的错,但是我们是产品经理,终止毒瘤的发展是我们的职责,等待大家保持冷静后再沟通。
因此产品经理需要时刻保持理性,并且减少抱怨与指责,更不能为自己找借口,必须克服所有障碍,解决所有问题。
四、成为出色的产品经理
一名出色的产品经理必然有着这样的头衔:执行力驱动者、工作效率提升者、团队合作促进者。
1、明确工作职责和目标
首先我们需要知道三个问题的答案:工作职责、工作目标、工作方向。
1.1、产品经理的工作职责就是探索产品的价值与挖掘需求并分析可用性、可行性。
1.2、产品经理的工作目标就是在最短的时间内理清产品需求,确定产品的逻辑关系。
1.3、产品经理的工作方向就是不断提升产品的价值。
2、了解团队成员的职责
在团队中有三个部门最为密切关联,分别是产品部、设计部、技术部。产品负责多方协调,设计往往有交互设计与视觉设计两种,统称用户体验设计,在设计人员的脑子里,更多的想的是美观风格。在团队当中,与技术人员的沟通最为吃力,所以我们更加需要了解技术人员的想法。技术人员往往不擅长用户体验设计,因为技术人员的脑子里想的是实现模型,而产品经理的脑子里想的是概念模型。
了解团队成员的职责和想法,有助于我们协调沟通,以他们的思维出发,将产品经理脑子里的概念模型写出来、画出来、讲出来。
3、明确任务重心和方向
当我们分配任务时,我们同时需要告诉对方任务的目标与用途,并且明确完成时间和优先级,这样有助于提升工作效率。
4、紧密的团队合作
从产品原型开始,团队之间就应该保持密切的合作,产品经理确定产品的需求,明确产品要做什么,然后调动大家积累参与到产品的设计当中。从设计部获得最佳的用户体验与交互设计方案,从技术部获得最新的技术动态,以便分析能否应用到产品当中。特别与技术部的合作更为密切,产品经理需要了解自己规划的产品原型能否实现,从技术层面能否实现。
5、不要轻易变更需求
在需求管理中已经写过,特别在产品进入开发阶段时,不要轻易变更需求。避免不断变更需求延误开发进度,有新需求时可以考虑在下一个版本中改进。
开发人员常常使用
单元测试来验证的一段儿代码的操作,很多时候单元测试可以检查抛出预期异常( expected exceptions)的代码。在Java语言中,
JUnit是一套标准的单元测试方案,它提供了很多验证抛出的异常的机制。本文就探讨一下他们的优点。
我们拿下面的代码作为例子,写一个测试,确保canVote() 方法返回true或者false, 同时你也能写一个测试用来验证这个方法抛出的IllegalArgumentException异常。
public class Student {
public boolean canVote(int age) {
if (i<=0) throw new IllegalArgumentException("age should be +ve");
if (i<18) return false;
else return true;
}
}
(Guava类库中提供了一个作参数检查的工具类--Preconditions类,也许这种方法能够更好的检查这样的参数,不过这个例子也能够检查)。
检查抛出的异常有三种方式,它们各自都有优缺点:
1.@Test(expected…)
@Test注解有一个可选的参数,"expected"允许你设置一个Throwable的子类。如果你想要验证上面的canVote()方法抛出预期的异常,我们可以这样写:
@Test(expected = IllegalArgumentException.class)
public void canVote_throws_IllegalArgumentException_for_zero_age() {
Student student = new Student();
student.canVote(0);
}
简单明了,这个测试有一点误差,因为异常会在方法的某个位置被抛出,但不一定在特定的某行。
2.ExpectedException
如果要使用JUnit框架中的ExpectedException类,需要声明ExpectedException异常。
@Rule
public ExpectedException thrown= ExpectedException.none();
然后你可以使用更加简单的方式验证预期的异常。
@Test
public void canVote_throws_IllegalArgumentException_for_zero_age() {
Student student = new Student();
thrown.expect(NullPointerException.class);
student.canVote(0);
}
或者可以设置预期异常的属性信息。
@Test
public void canVote_throws_IllegalArgumentException_for_zero_age() {
Student student = new Student();
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("age should be +ve");
student.canVote(0);
}
除了可以设置异常的属性信息之外,这种方法还有一个优点,它可以更加精确的找到异常抛出的位置。在上面的例子中,在构造函数中抛出的未预期的(unexpected) IllegalArgumentException 异常将会引起测试失败,我们希望它在canVote()方法中抛出。
从另一个方面来说,如果不需要声明就更好了
@Rule
public ExpectedException thrown= ExpectedException.none();
它就像不需要的噪音一样,如果这样就很好了
expect(RuntimeException.class)
或者:
expect(RuntimeException.class, “Expected exception message”)
或者至少可以将异常和信息当做参数传进去
thrown.expect(IllegalArgumentException.class, “age should be +ve”);
3.Try/catch with assert/fail
在JUnit4之前的版本中,使用try/catch语句块检查异常
@Test public void canVote_throws_IllegalArgumentException_for_zero_age() { Student student = new Student(); try { student.canVote(0); } catch (IllegalArgumentException ex) { assertThat(ex.getMessage(), containsString("age should be +ve")); } fail("expected IllegalArgumentException for non +ve age"); } |
尽管这种方式很老了,不过还是非常有效的。主要的缺点就是很容易忘记在catch语句块之后需要写fail()方法,如果预期异常没有抛出就会导致信息的误报。我曾经就犯过这样的错误。
总之,这三种方法都可以测试预期抛出的异常,各有优缺点。对于我个人而言,我会选择第二种方法,因为它可以非常精确、高效的测试异常信息。
English » | | | | | | | | |
问题
[Simplified Chinese] 使用
IBM Rational AppScan Standard版本对Web程序进行扫描时,如何对发生的通信问题进行分析?
症状
现象 1
在扫描过程中扫描日志(选择菜单[查看] > [扫描日志]或者从Rational AppScan Standard安装路径下的Logs文件夹中打开ScanLog.log文件)可能会显示以下格式的错误消息:
[DATE], [TIME]: Cannot connect to host: [IP/HOSTNAME]...
[DATE], [TIME]:
Test [TEST_NUMBER] [TEST_NAME] failed due to communication error: [PATH]...
[DATE], [TIME]: Connection established with host: [IP/HOSTNAME]
现象 2
或者在AppScan系统日志(选择菜单[帮助] > [AppScan 日志]或者从Rational AppScan Standard安装路径下的Logs文件夹中打开AppScanSys.log文件)中可能会发生如下的错误消息:
[DAY_OF_WEEK], [DATE], [TIME] Warning: Communication problems occurred while testing URL '[PATH]' for '[TEST_NAME]'
同时也可能看到如下消息:
[DAY_OF_WEEK], [DATE], [TIME] System : Server [IP/HOSTNAME] is not responding...
[DAY_OF_WEEK], [DATE], [TIME] System : Server [IP/HOSTNAME] is responding again
或者如下消息:
[DAY_OF_WEEK], [DATE], [TIME] System : Proxy [IP/HOSTNAME] is not responding
[DAY_OF_WEEK], [DATE], [TIME] System : Proxy [IP/HOSTNAME] is responding again
原因
通常由于以下原因会导致发生通信问题:
原因 1
在Rational AppScan Standard的主机上安装的个人防火墙或防病毒软件会屏蔽向外发送的信息。
原因 2
企业防火墙软件会将AppScan发送的请求当作对网站的攻击从而切断连接。
原因 3
当Rational AppScan Standard对于响应速度慢的Web服务器发送大量的请求时,请求会被服务器拒绝。
解决问题
原因1的解决方案:
原因2的解决方案:
在系统管理员的协助下需要确认企业防火墙是否真正切断AppScan的连接。通常当网络中安装了新的代理服务器或防火墙时,会屏蔽请求从而降低Rational AppScan Standard的
工作效率。
原因3的解决方案:
1.在[扫描配置] > [通信和代理]中,将[线程数]降为1同时将[超时]增大为30秒。如果能够消除通信错误消息的话,可以少量增加线程的数目。
2.可以设定Rational AppScan Standard使用HTTP/1.1来解决通信问题。关于设定AppScan使用HTTP/1.1更多的内容,请参照Technote 1298662: Forcing Rational AppScan Standard to use HTTP/1.1。
English » | | | | | | | | |
作者认为这是最具争议的项目, 他认为这是一个神话: "
测试人员只需要一点, 或是没有程序撰写的知识". 这是行不通的, 可是很不幸的, 这是目前一般常见的状况. 这里作者提出两个主要原因
(1) 测试人员是在测试软件程序, 没有程序设计的知识, 他们无法洞察bugs真正的原因, 找出最可能发生问题的地方. 测试通常没有足够的时间, 去达到真正测试的"完整", 所以
软件测试是需要在现有资源和彻底性之间做出某种妥协. 测试人员要如何优化有限的资源, 针对最可能发生问题的地方做测试呢? 如果他没有程序设计的知识, 他不可能有正确的直觉, 去知道要去哪里找到这些地方
(2) 所有最简单的测试方法都是tool- and technology-intensive. 基本上, 这些工具都是需要程序撰写或设计的知识, 才知道怎样用的好. 同理, testing techniques也是一样. 若是你没这些知识, 你可能只能用一些ad hoc的testing techniques和最简单的工具
此外作者还认为找entry level的程序设计人员来当测试人员, 这并不是好的主意. 原因如下:
(1) 失败者的形象 (Loser Image)
Entry-level的人会期待得到一个开发人员的
工作, 如果他们不能找到这样的职位, 他们会觉得是一种失败. 这种情况在测试团队会更明显
(2) 开发人员对你没有信任感 (Credibility With Programmers)
测试人员所面对的开发人员往往比他们资深很多, 除非他们之前所学的东西十分扎实, 否则他们所会的东西, 在开发人员面前会只是玩具而已. 因此他们没有什么公信力, 去提供开发人员什么有用的信息.
(3) 测试人员不懂诀窍(Just Plain Know-How)
基本上, 这些测试人员不懂程序实际上怎么撰写, 那是开发人员的本业. 如果你是开发人员, 资深的人还会去带刚入门的人怎么做. 若是你是测试人员, 你只会去学 doing a build, configuration control, procedures, process等等. 都是在
学习怎么做, 而不是真正去做它. 所以无法真正懂得那些诀窍
English » | | | | | | | | |
首先说介绍一下,Assert类所在的命名空间为Microsoft.VisualStudio.TestTools.UnitTesting 在工程文件中只要引用Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll就可以使用了,在这里我举例说明Assert里面的一些主要的静态成员。
1、 AreEqual:方法被重载了N多次,主要功能是判断两个值是否相等;如果两个值不相等,则
测试失败。
2、 AreNotEqual:方法被重载了N多次,主要功能是判断两个值是否不相等;如果两个值相等,则测试失败。
3、 AreNotSame:引用的对象是否不相同;如果两个输入内容引用相同的对象,则测试失败.
4、 AreSame:引用的对象是否相同;如果两个输入内容引用不相同的对象,则测试失败.
5、 Fail:断言失败。
6、 Inconclusive:表示无法证明为 true 或 false 的测试结果
7、 IsFalse:指定的条件是否为 false;如果该条件为 true,则测试失败。
8、 IsTrue:指定的条件是否为 true;如果该条件为 false,则测试失败
9、 IsInstanceofType:测试指定的对象是否为所需类型的实例;如果所需的实例不在该对象的继承层次结构中,则测试失败
10、IsNotInstanceofType: 测试指定的对象是否为所需类型的实例;如果所需的实例在该对象的继承层次结构中,则测试失败
11、IsNull:测试指定的对象是否为非空
12、IsNotNull:测试指定的对象是否为非空
English » | | | | | | | | |
Text-to-speech function is limited to 100 characters
soapUI Pro指标项说明:
Sets the startup delay for each thread (in milliseconds), setting to 0 will start all threads simultaneously. min The shortest time the step has taken (in milliseconds). max The longest time the step has taken (in milliseconds). avg The average time for the test step (in milliseconds). last The last time for the test step (in milliseconds). cnt The number of times the test step has been executed. tps The number of transactions per second for the test step, see Calculation of TPS/BPS below. bytes The number of bytes processed by the test step. bps The bytes per second processed by the test step. err The number of assertion errors for the test step. rat Failed requests ratio (the percentage of requests that failed). |
1、Test Step:调用方法名称。
2、min、max、avg、last:调用时的最小、最大、平均、最近一次的响应时间
3、cnt总调用次数 ;tps平均每秒调用次数
4、bytes接口处理的字符数;bps平均每秒接口处理的字符数
5、err报错次数;rat报错次数/执行次数
或
min,最小响应时间
max,最大响应时间
avg,平均响应时间
last,上一次请求响应时间
cnt,请求数
tps,每秒处理请求数
bps,吞吐率
rat,错误率
为了
测试这个存储过程,我遥了一圈去做这个事情,这里说一下我自己接受到任务和自己开始是怎么想的。
方法一:
一开始我想着可以使用C#直接去调用存储过程,然后用
Loadrunner调用C#的dll去测试,后来发现找不到LoadRunner怎样直接调用C#写的dll;可是dll存储过程都已经写好,不可能推倒重新用其他的方式去做,由于任务时间比较紧,就山寨的用C#写了个 .exe 去调用 dll,完成后执行,印象是:
数据库跟本一点压力都没有,可是负载机都已经 100%了,并且这种做法得需要在每台负载机都安装一个
oracle 客户端,要不访问不了,负载机有 30台,每台都去安装一编,我倒!
方法二:
最后想到一种方法,写一个WebService 去调用Dll,Loadrunner再去调用WebService去进行测试。(这种做法是不是很笨?有没有其他更好的方法,请留言给我!)
下面就把整个过程详细列一下,其实这个之前也都有做过,但由于没有记录到博客,全忘记了,重写是多么的痛苦,血的教训说明:写博客还是有必需的!
Step1:编写访问存储过程的dll
using System; using System.Collections.Generic; using System.Data; using System.Data.OracleClient; using System.Linq; using System.Text; namespace PaysysInterfaceTest { public class PaysysBase { public OracleConnection conn = null; public OracleCommand cmd = null; /// <summary> /// 数据库初始化 /// </summary> /// <param name="DataSource">数据库源(Orlacle Client下的tnsnames.ora配置</param> /// <param name="DataUserId">数据库登录名</param> /// <param name="DataPassword">数据库登录密码</param> /// <returns></returns> public int PaysysInit(string DataSource, string DataUserId, string DataPassword) { var mConn = string.Format("Data Source={0};User Id={1};Password={2};", DataSource, DataUserId, DataPassword); conn = new OracleConnection(mConn); try { conn.Open(); cmd = new OracleCommand(); cmd.Connection = conn; } catch (Exception ex) { return 0; } return 1; } /// <summary> /// 调用的存储过程 /// </summary> /// <param name="interfaceName">存储过程名称</param> /// <returns></returns> public int CallInterface(string interfaceName) { var nResult = 0; var queryString = interfaceName; cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = queryString; try { cmd.ExecuteNonQuery(); Console.WriteLine("Query Success!!"); nResult = Convert.ToInt32(cmd.Parameters["result"].Value); } catch (Exception ex) { Console.WriteLine("Query Error!!\r\n" + ex.Message); nResult = Convert.ToInt32(cmd.Parameters["result"].Value); ; } finally { cmd.Clone(); conn.Close(); Console.WriteLine("Close Db"); } return nResult; } } public class PlayerLogin : PaysysBase { public string s_account = ""; public string s_password = ""; /// <summary> /// 存储过程初始化参数 /// </summary> /// <param name="account">用户账号</param> /// <param name="password">用户密码</param> public void LoginInit(string account, string password) { s_account = account; s_password = password; InitParam(); } /// <summary> /// 初始化信息 /// </summary> private void InitParam() { //反回值 cmd.Parameters.Add("result", OracleType.Float); cmd.Parameters["result"].Direction = ParameterDirection.ReturnValue; cmd.Parameters.Add("s_account", OracleType.VarChar); cmd.Parameters["s_account"].Direction = ParameterDirection.Input; cmd.Parameters["s_account"].Value = s_account; cmd.Parameters.Add("s_password", OracleType.VarChar); cmd.Parameters["s_password"].Direction = ParameterDirection.Input; cmd.Parameters["s_password"].Value = s_password; //==================out================== cmd.Parameters.Add("d_login_time", OracleType.DateTime); cmd.Parameters["d_login_time"].Direction = ParameterDirection.Output; cmd.Parameters.Add("d_last_login_time", OracleType.DateTime); cmd.Parameters["d_last_login_time"].Direction = ParameterDirection.Output; cmd.Parameters.Add("d_last_logout_time", OracleType.DateTime); cmd.Parameters["d_last_logout_time"].Direction = ParameterDirection.Output; } |
// <summary> /// 调用执行存储过程 /// </summary> /// <returns></returns> public int LoginInterface() { return CallInterface("player.login"); } } public class PlayerLogout : PaysysBase { public string s_account = ""; public void LogoutInit(string account) { s_account = account; InitParam(); } private void InitParam() { //反回值 cmd.Parameters.Add("result", OracleType.Float); cmd.Parameters["result"].Direction = ParameterDirection.ReturnValue; cmd.Parameters.Add("s_account", OracleType.VarChar); cmd.Parameters["s_account"].Direction = ParameterDirection.Input; cmd.Parameters["s_account"].Value = s_account; } public int LogoutInterface() { return CallInterface("player.logout"); } } } |
遇到问题:
1、在编写login的接口时,因为只初始化了Input的参数,还以为不需要Output的参数,所以导致调用的时候一直出现参数缺少的问题。入参和出参都需要填!
Setp2:WebServices调用dll
1、新建工程:
2、新建好工程后添加刚刚写好的dll
3、WebService调用代码
[WebMethod] public string PaysysLogin(string dbName, string dbAccount, string dbPassword, string account, string password) { var nResult = 0; try { PlayerLogin login = new PlayerLogin(); login.PaysysInit(dbName, dbAccount, dbPassword); login.LoginInit(account, password); nResult = login.LoginInterface(); } catch (Exception ex) { return ex.Message; } return nResult.ToString(); } [WebMethod] public string PaysysLogout(string dbName, string dbAccount, string dbPassword, string account) { var nResult = 0; try { PlayerLogout logout = new PlayerLogout(); logout.PaysysInit(dbName, dbAccount, dbPassword); logout.LogoutInit(account); nResult = logout.LogoutInterface(); } catch (Exception ex) { return ex.Message; } return nResult.ToString(); } |
之后就可以调试一下了!
Setp3:部署到IIS(可以查看我之前的一编博客,是一样的)
http://www.cnblogs.com/Martin_Q/archive/2010/12/06/1897614.html
调试结果
遇到问题:
这里遇到一个很糟糕的问题,使用IIS在调用到:conn.Open() 的时候出现一个错误:system.data.oracleclient 需要 oracle 客户端软件 8.1.7 或更高版本。
这个问题一定要值得注意,开始我以为是iis没有权限访问oracle目录,我为什么会这么认为呢?因为我为了查这个问题,我专门写了一个.exe 去执行调用,exe应用程序执行成功,iis出错!
原来问题是由于我安装的oracle客户端为简易版本,很多很多的dll都没有。可以网上找一下:10201_client_win32.zip 些版本client,当然还有其他的权限问题!这里没遇到就不谈了
Setp4:使用LoadRunner调用WebServices
1、Loadrunner新建WebService
2、点ManageServices 添加你的WebService地址
3、添加WebService 调用接口
搭建的地址为:http://10.20.87.62:81/Service.asmx 为什么后面需要手工加一个:?WSDL 呢?(知道的同学说一下!)
4、添加成功
5、添加对应的存储过程
6、添加成功后Action代码
Action() { lr_start_transaction("Login"); web_service_call( "StepName=PaysysLogin_101", "SOAPMethod=Service|ServiceSoap|PaysysLogin", "ResponseParam=response", "Service=Service", "ExpectedResponse=SoapResult", "Snapshot=t1386150115.inf", BEGIN_ARGUMENTS, // 参数传入 "dbName=orcl_35", "dbAccount=root", "dbPassword=123456", "account={Account}", "password=a", END_ARGUMENTS, BEGIN_RESULT, // 获取返回值 "PaysysLoginResult=LoginResult", END_RESULT, LAST); lr_message("All:%s\r\nLoginResult:%s\r\n",lr_eval_string("{response}"),lr_eval_string("{LoginResult}")); if(atoi(lr_eval_string("{LoginResult}")) == 1) { lr_end_sub_transaction("Login",LR_PASS); } else { lr_end_sub_transaction("Login",LR_FAIL); lr_error_message("Login Error:%s ---- %s",lr_eval_string("{LoginResult}"),lr_eval_string("{Account}")); } return 0; } |
输出结果:
All:<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><PaysysLoginResponse xmlns="http://tempuri.org/"><PaysysLoginResult>1</PaysysLoginResult></PaysysLoginResponse></soap:Body></soap:Envelope>
LoginResult:1
为了
测试这个存储过程,我遥了一圈去做这个事情,这里说一下我自己接受到任务和自己开始是怎么想的。
方法一:
一开始我想着可以使用C#直接去调用存储过程,然后用
Loadrunner调用C#的dll去测试,后来发现找不到LoadRunner怎样直接调用C#写的dll;可是dll存储过程都已经写好,不可能推倒重新用其他的方式去做,由于任务时间比较紧,就山寨的用C#写了个 .exe 去调用 dll,完成后执行,印象是:
数据库跟本一点压力都没有,可是负载机都已经 100%了,并且这种做法得需要在每台负载机都安装一个
oracle 客户端,要不访问不了,负载机有 30台,每台都去安装一编,我倒!
方法二:
最后想到一种方法,写一个WebService 去调用Dll,Loadrunner再去调用WebService去进行测试。(这种做法是不是很笨?有没有其他更好的方法,请留言给我!)
下面就把整个过程详细列一下,其实这个之前也都有做过,但由于没有记录到博客,全忘记了,重写是多么的痛苦,血的教训说明:写博客还是有必需的!
Step1:编写访问存储过程的dll
using System; using System.Collections.Generic; using System.Data; using System.Data.OracleClient; using System.Linq; using System.Text; namespace PaysysInterfaceTest { public class PaysysBase { public OracleConnection conn = null; public OracleCommand cmd = null; /// <summary> /// 数据库初始化 /// </summary> /// <param name="DataSource">数据库源(Orlacle Client下的tnsnames.ora配置</param> /// <param name="DataUserId">数据库登录名</param> /// <param name="DataPassword">数据库登录密码</param> /// <returns></returns> public int PaysysInit(string DataSource, string DataUserId, string DataPassword) { var mConn = string.Format("Data Source={0};User Id={1};Password={2};", DataSource, DataUserId, DataPassword); conn = new OracleConnection(mConn); try { conn.Open(); cmd = new OracleCommand(); cmd.Connection = conn; } catch (Exception ex) { return 0; } return 1; } /// <summary> /// 调用的存储过程 /// </summary> /// <param name="interfaceName">存储过程名称</param> /// <returns></returns> public int CallInterface(string interfaceName) { var nResult = 0; var queryString = interfaceName; cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = queryString; try { cmd.ExecuteNonQuery(); Console.WriteLine("Query Success!!"); nResult = Convert.ToInt32(cmd.Parameters["result"].Value); } catch (Exception ex) { Console.WriteLine("Query Error!!\r\n" + ex.Message); nResult = Convert.ToInt32(cmd.Parameters["result"].Value); ; } finally { cmd.Clone(); conn.Close(); Console.WriteLine("Close Db"); } return nResult; } } public class PlayerLogin : PaysysBase { public string s_account = ""; public string s_password = ""; /// <summary> /// 存储过程初始化参数 /// </summary> /// <param name="account">用户账号</param> /// <param name="password">用户密码</param> public void LoginInit(string account, string password) { s_account = account; s_password = password; InitParam(); } /// <summary> /// 初始化信息 /// </summary> private void InitParam() { //反回值 cmd.Parameters.Add("result", OracleType.Float); cmd.Parameters["result"].Direction = ParameterDirection.ReturnValue; cmd.Parameters.Add("s_account", OracleType.VarChar); cmd.Parameters["s_account"].Direction = ParameterDirection.Input; cmd.Parameters["s_account"].Value = s_account; cmd.Parameters.Add("s_password", OracleType.VarChar); cmd.Parameters["s_password"].Direction = ParameterDirection.Input; cmd.Parameters["s_password"].Value = s_password; //==================out================== cmd.Parameters.Add("d_login_time", OracleType.DateTime); cmd.Parameters["d_login_time"].Direction = ParameterDirection.Output; cmd.Parameters.Add("d_last_login_time", OracleType.DateTime); cmd.Parameters["d_last_login_time"].Direction = ParameterDirection.Output; cmd.Parameters.Add("d_last_logout_time", OracleType.DateTime); cmd.Parameters["d_last_logout_time"].Direction = ParameterDirection.Output; } |
// <summary> /// 调用执行存储过程 /// </summary> /// <returns></returns> public int LoginInterface() { return CallInterface("player.login"); } } public class PlayerLogout : PaysysBase { public string s_account = ""; public void LogoutInit(string account) { s_account = account; InitParam(); } private void InitParam() { //反回值 cmd.Parameters.Add("result", OracleType.Float); cmd.Parameters["result"].Direction = ParameterDirection.ReturnValue; cmd.Parameters.Add("s_account", OracleType.VarChar); cmd.Parameters["s_account"].Direction = ParameterDirection.Input; cmd.Parameters["s_account"].Value = s_account; } public int LogoutInterface() { return CallInterface("player.logout"); } } } |
遇到问题:
1、在编写login的接口时,因为只初始化了Input的参数,还以为不需要Output的参数,所以导致调用的时候一直出现参数缺少的问题。入参和出参都需要填!
Setp2:WebServices调用dll
1、新建工程:
2、新建好工程后添加刚刚写好的dll
3、WebService调用代码
[WebMethod] public string PaysysLogin(string dbName, string dbAccount, string dbPassword, string account, string password) { var nResult = 0; try { PlayerLogin login = new PlayerLogin(); login.PaysysInit(dbName, dbAccount, dbPassword); login.LoginInit(account, password); nResult = login.LoginInterface(); } catch (Exception ex) { return ex.Message; } return nResult.ToString(); } [WebMethod] public string PaysysLogout(string dbName, string dbAccount, string dbPassword, string account) { var nResult = 0; try { PlayerLogout logout = new PlayerLogout(); logout.PaysysInit(dbName, dbAccount, dbPassword); logout.LogoutInit(account); nResult = logout.LogoutInterface(); } catch (Exception ex) { return ex.Message; } return nResult.ToString(); } |
之后就可以调试一下了!
Setp3:部署到IIS(可以查看我之前的一编博客,是一样的)
http://www.cnblogs.com/Martin_Q/archive/2010/12/06/1897614.html
调试结果
遇到问题:
这里遇到一个很糟糕的问题,使用IIS在调用到:conn.Open() 的时候出现一个错误:system.data.oracleclient 需要 oracle 客户端软件 8.1.7 或更高版本。
这个问题一定要值得注意,开始我以为是iis没有权限访问oracle目录,我为什么会这么认为呢?因为我为了查这个问题,我专门写了一个.exe 去执行调用,exe应用程序执行成功,iis出错!
原来问题是由于我安装的oracle客户端为简易版本,很多很多的dll都没有。可以网上找一下:10201_client_win32.zip 些版本client,当然还有其他的权限问题!这里没遇到就不谈了
Setp4:使用LoadRunner调用WebServices
1、Loadrunner新建WebService
2、点ManageServices 添加你的WebService地址
3、添加WebService 调用接口
搭建的地址为:http://10.20.87.62:81/Service.asmx 为什么后面需要手工加一个:?WSDL 呢?(知道的同学说一下!)
4、添加成功
5、添加对应的存储过程
6、添加成功后Action代码
Action() { lr_start_transaction("Login"); web_service_call( "StepName=PaysysLogin_101", "SOAPMethod=Service|ServiceSoap|PaysysLogin", "ResponseParam=response", "Service=Service", "ExpectedResponse=SoapResult", "Snapshot=t1386150115.inf", BEGIN_ARGUMENTS, // 参数传入 "dbName=orcl_35", "dbAccount=root", "dbPassword=123456", "account={Account}", "password=a", END_ARGUMENTS, BEGIN_RESULT, // 获取返回值 "PaysysLoginResult=LoginResult", END_RESULT, LAST); lr_message("All:%s\r\nLoginResult:%s\r\n",lr_eval_string("{response}"),lr_eval_string("{LoginResult}")); if(atoi(lr_eval_string("{LoginResult}")) == 1) { lr_end_sub_transaction("Login",LR_PASS); } else { lr_end_sub_transaction("Login",LR_FAIL); lr_error_message("Login Error:%s ---- %s",lr_eval_string("{LoginResult}"),lr_eval_string("{Account}")); } return 0; } |
输出结果:
All:<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><PaysysLoginResponse xmlns="http://tempuri.org/"><PaysysLoginResult>1</PaysysLoginResult></PaysysLoginResponse></soap:Body></soap:Envelope>
LoginResult:1
python写的数据采集,对一般有规律的页面用 urllib2 + BeautifulSoup + 正则就可以搞定。 但是有些页面的内容是通过js生成,或者通过js跳转的,甚至js中还加入几道混淆机制;对这种涉及页面脚本解析的内容,前面的方式便很无力。
这时我们需要能解析、运行js的引擎——浏览器,而python selenium能提供程序与浏览器的交互接口,再加上phantomjs这个可以后台运行的浏览器,即使用 selenium + phantomjs 便可以解决以上的问题。
selenium可以操作页面的元素,并且提供执行js脚本的接口。但其调用js脚本后并不能直接返回执行的结果,这样再采集内容的过程中就会受到一些限制。 比如我们想使用页面中的函数进行数据转换,或者获取iframe里的内容,这些js产生数据要传回比较麻烦。
所以我便写一个简化js数据回传的扩展 exescript.py
#!/usr/bin/env python # -*- coding:utf-8 -*- # # created by heqingpan _init_js=""" (function (){ if (window.__e) { return; } var e=document.createElement('div'); e.setAttribute("id","__s_msg"); e.style.display="none"; document.body.appendChild(e); window.__e=e; })(); window.__s_set_msg=function(a){ window.__e.setAttribute("msg",a.toString()||""); } """ _loadJsFmt=""" var script = document.createElement('script'); script.src = "{0}"; document.body.appendChild(script); """ _jquery_cdn="http://lib.sinaapp.com/js/jquery/1.7.2/jquery.min.js" _warpjsfmt="__s_set_msg({0})" class ExeJs(object): def __init__(self,driver,trytimes=10): from time import sleep self.driver=driver driver.execute_script(_init_js) while trytimes >0: try: self.msgNode=driver.find_element_by_id('__s_msg') break except Exception: sleep(1) trytimes -= 1 if self.msgNode is None: raise Exception() def exeWrap(self,jsstr): """ jsstr 执行后有返回值,返回值通过self.getMsg()获取 """ self.driver.execute_script(_warpjsfmt.format(jsstr)) def loadJs(self,path): self.execute(_loadJsFmt.format(path)) def loadJquery(self,path=_jquery_cdn): self.loadJs(path) def execute(self,jsstr): self.driver.execute_script(jsstr) def getMsg(self): return self.msgNode.get_attribute('msg') |
打开ipython上一个例子,获取博客园首页文章title列表
from selenium import webdriver import exescript d=webdriver.PhantomJS("phantomjs") d.get("http://www.cnblogs.com/") exejs=exescript.ExeJs(d) exejs.exeWrap('$(".post_item").length') print exejs.getMsg() #out: """ 20 """ jsstr="""(function(){ var r=[]; $(".post_item").each(function(){ var $this=$(this); var $h3=$this.find("h3"); r.push($h3.text()); }); return r.join(',');})()""" exejs.exeWrap(jsstr) l=exejs.getMsg() for title in l.split(','): print title #out: """ mac TeamTalk开发点点滴滴之一——DDLogic框架分解上 The directfb backend was supported together with linux-fb backend in GTK+2.10 Science上发表的超赞聚类算法 功能齐全、效率一流的免费开源数据库导入导出工具(c#开发,支持SQL server、SQLite、ACCESS三种数据 库),每月借此处理数据5G以上 企业级应用框架(三)三层架构之数据访问层的改进以及测试DOM的发布 Unity3D 第一季 00 深入理解U3D开发平台 Welcome to Swift (苹果官方Swift文档初译与注解二十一)---140~147页(第三章--集合类型) appium简明教程(11)——使用resource id定位 SQL语句汇总(终篇)—— 表联接与联接查询 fopen警告处理方式 AndroidWear开发之HelloWorld篇 AMD and CMD are dead之KMD.js版本0.0.2发布 SQL语句汇总(三)——聚合函数、分组、子查询及组合查询 DevExpress GridControl功能总结 ASP.NET之Jquery入门级别 2014年前端面试经历 grunt源码解析:整体运行机制&grunt-cli源码解析 跟用户沟通,问题尽量分析清楚,以及解决问题 ASP.NET之Ajax系列(一) 算法复杂度分析 """ |
具体需求: 有一个登陆页面, (假如上面有2个textbox, 一个提交按钮。 请针对这个页面设计30个以上的
test case.)
此题的考察目的:
面试者是否熟悉各种
测试方法,是否有丰富的
Web测试经验, 是否了解Web开发,以及设计Test case的能力
这个题目还是相当有难度的, 一般的人很难把这个题目回答好。
首先,你要了解用户的需求,比如这个登录界面应该是弹出窗口式的,还是直接在网页里面。对用户名的长度,和密码的强度(就是是不是必须多少位,大小写,特殊字符混搭)等。还有比如用户对界面的美观是不是有特殊的要求?(即是否要进行UI测试)。剩下的就是设计用例了 ,等价类,边界值等等。
请你记住一点,任何测试,不管测什么都是从了解需求开始的。
0. 什么都不输入,点击提交按钮,看提示信息。
1.输入正确的用户名和密码,点击提交按钮,验证是否能正确登录。
2.输入错误的用户名或者密码, 验证登录会失败,并且提示相应的错误信息。
3.登录成功后能否能否跳转到正确的页面
4.用户名和密码,如果太短或者太长,应该怎么处理
5.用户名和密码,中有特殊字符(比如空格),和其他非英文的情况
6.记住用户名的功能
7.登陆失败后,不能记录密码的功能
8.用户名和密码前后有空格的处理
9.密码是否加密显示(星号圆点等)
10.牵扯到验证码的,还要考虑文字是否扭曲过度导致辨认难度大,考虑颜色(色盲使用者),刷新或换一个按钮是否好用
11.登录页面中的注册、忘记密码,登出用另一帐号登陆等链接是否正确
12.输入密码的时候,大写键盘开启的时候要有提示信息。
1.布局是否合理,2个testbox 和一个按钮是否对齐
2.testbox和按钮的长度,高度是否复合要求
3. 界面的设计风格是否与UI的设计风格统一
4. 界面中的文字简洁易懂,没有错别字。
1.打开登录页面,需要几秒
2.输入正确的用户名和密码后,登录成功跳转到新页面,不超过5秒
安全性测试(Security test)
1.登录成功后生成的Cookie,是否是httponly (否则容易被脚本盗取)
2.用户名和密码是否通过加密的方式,发送给Web服务器
3.用户名和密码的验证,应该是用服务器端验证, 而不能单单是在客户端用javascript验证
4.用户名和密码的输入框,应该屏蔽
SQL 注入攻击
5.用户名和密码的的输入框,应该禁止输入脚本 (防止XSS攻击)
6.错误登陆的次数限制(防止暴力破解)
7. 考虑是否支持多用户在同一机器上登录;
8. 考虑一用户在多台机器上登录
可用性测试(Usability Test)
1. 是否可以全用键盘操作,是否有快捷键
2. 输入用户名,密码后按回车,是否可以登陆
3. 输入框能否可以以Tab键切换
兼容性测试(Compatibility Test)
1.主流的浏览器下能否显示正常已经功能正常(IE,6,7,8,9, Firefox, Chrome, Safari,等)
3.移动设备上是否正常工作,比如Iphone, Andriod
4.不同的分辨率
本地化测试 (Localization test)
1. 不同语言环境下,页面的显示是否正确。
软件辅助性测试 (Accessibility test)
软件辅助功能测试是指测试软件是否向残疾用户提供足够的辅助功能
1. 高对比度下能否显示正常 (视力不好的人使用)