qileilove

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

测试提前—技术方案评审

测试提前进行的越深入,越体会到了解系统架构的重要性,参与到技术方案评审,不仅是听,还要评,进一步学会审。这个阶段可以更关注可测性、性能考虑、可拓展性等
  举几个技术方案阶段关注并改进的例子.
  性能考虑
  关注方向:系统调用、单个\批量,串行\并行,读tair\读db
  例子:
  qc系统资质验证的过程是,业务系统发起验证一颗资质树(多个资质)的请求,资质系统获取请求后,从多个业务方系统获取数据并和要求值进行对比,将对比验证结果返回到业务系统
  以下是技术方案时对老系统的改进.
  1. 单条验证 -> 提供批量验证接口,避免多次HSF调用
  2. 单颗资质树资质获取 -> 资质数据读取方式从原有的懒加载改为预加载。合并多个资质树的资质,一次读取
  3. 串行读取 -> 并行数据读取。资质数据涉及多个系统,将多个HSF调用从串行改为并行
  4. 串行验证 -> 并行验证。批量验证时采用并行的方式验证
  5. 提供服务方式:HSF -> JAR,本地调用和hsf调用的性能差别
  6. 缓存读取方式:只读取所需 -> 读取所有,减少二次读取时对DB的访问
 DB设计
  关注方向:避免分库查询、分表查询、多表查询
  例子:
  服务评价项目的项目目标是对客服小二处理case的服务进行评价。record表(评价任务表,一个case对应买卖家共两条record记录即两个评价 问卷)、answer表(买卖家的答卷记录,一个问卷对应多条答案记录,recordId作为answer表外键),record为单表,answer分 表按recordId进行分表
  问题点在answer表的分表是按照recordId进行取模分表。
  这种方式下,查询一个case对应的评价记录:先根据caseId查询record表获得两个recordId,再根据recordId取模查询两个answer表的记录,再返回结果
  改进方案是:在answer表增加一个caseId字段,根据caseId分表,这样查询答题记录只一个caseId查询一个answer表即获取买卖家答题记录。只查询一次和查询两次且有分表查询的对比,效率提升是显而易见的
  hsf设计
  关注方向:异常处理
  例子:
  服务评价系统对外提供一个重要hsf服务,业务系统case在完结时调用该hsf服务触发评价任务开启。下图是开发设计的调用流程, 主要关注红框中的调用方式。
  业务case完结是业务主流程,开启评价是分支流程,分支流程应该是不能影响到主流程的,一个p1级应用最好不要去依赖一个p3级应用。所以,评价系统的 hsf服务不能抛任何异常给业务系统,hsf服务需要catch所有异常并包装一个统一的返回值给业务系统,这种设计方式下,除非系统挂了服务找不到了才 可能对业务系统产生影响

posted @ 2014-03-10 11:09 顺其自然EVO 阅读(285) | 评论 (0)编辑 收藏

黑盒设计测试用例方法

 日常的测试工作中都在有形无形的应用各种测试方法进行测试,只是没有形成完整的体系概念。这几天将 测试用例设计方法进行汇总,将测试思想运用于实际工作中,从而更好的指导测试工作。
  首先汇总日常最常用的三种方法:等价类划分、边界值分析法、错误推断法。
  1.等价类划分
  在软件测试中,穷举法虽然是最安全最保险的一种方法但成本代价高,一般是不可取的。我们可以通过等价类划分方法花费最小的代价来完成最高效的测试。
  等价类划分是把程序输入域划分成若干子集,然后从子集中选取少数具有代表性的数据进行测试。在子集集合中,各个输入数据对于揭露程序中的错误是等价的。等价类分为有效等价类和无效等价类。
  1.1有效等价类
  对于程序规格来说合理的、有意义的输入数据的集合,检验程序是否实现了规格说明中的功能和性能。
  1.2无效等价类
  不合理的、无意义的输入数据集合,验证程序处理意外数据的能力。
  1.3划分方法
  划分等价类时,可分为按区间划分、按数值划分、按数值集合划分、按限制条件和规则划分、按处理方式划分。除了应掌握必须使同类数据的处理过程及处理结果完全一致的大原则,可参考以下划分方法:
  1)  输入条件规定了取值范围或值的个数的情况下,可以确定一个有效等价类和两个无效等价类,如合格成绩取值范围为[60,100],则范围内取值为有效等价类,范围外<60和>100为无效等价类
  2)  输入条件规定了输入值的集合或“必须如何”的情况下,可以确定一个有效等价类和一个无效等价类,如:规定数据库类型必须选择oracle,则选择oracle时为有效等价类,否则为无效等价类
  3)  输入条件是一个布尔量的情况下,可以确定一个有效等价类和一个无效等价类
  4)  输入条件规定必须遵守某种规则的情况下,可以确定一个有效等价类和若干个无效等价类(从不同角度违法规则),如:规定输入必须为非0正整数,则无效等价类可以分为空、0、负整数、小数、字符等
  5)  在规定了输入数据的一组值(假定N个),并且程序要对每个输入值分别处理的情况下,可以确立N个有效等价类和一个无效等价类。如下列框选择“科目”,每个科目所显示的信息不同。
  6)  在确知已划分的等价类中各元素在程序处理镇南关的方式不同的情况下,则应再将该等价类进一步的划分为更小的等价类
  1.4等价类表
  在确立了等价类后,可以建立等价类表,列出所有划分出的等价类
1.5设计测试用例
  然后从划分出的等价类中按以下原则设计测试用例:
  1)为每个等价类规定一个唯一编号
  2)设计一个新的测试用例,使其尽可能多得覆盖尚未被覆盖的有效等价类,重复这一步,直到所有的有效等价类都被覆盖为止
  3)设计一个新的测试用例,使其仅覆盖一个尚未被覆盖的无效等价类,重复这一步,直到所有无效等价类都被覆盖为止
  2.边界值分析法
  以往的测试经验表明,由于需求界定不准确、设计不严密、程序书写手误等等原因,对于这些数据范围边界的判断是软件极容易出错的地方。大量的错误往往发生在输入或输出范围的边界上,因此针对各种边界情况设计测试用例,可以检查出更多的错误。
  2.1边界值适用场景
  边界值法多被应用于以上几个场景中:
  输入(输出)条件规定了取值范围
  输入(输出)条件规定了值的个数
  程序规格说明书中提到的输入或输出是一个有序的集合
  程序中使用了一个内部数据结构
  边界值取值应当选取正好等于、刚刚大于最大边界值和刚刚小于最小边界值最为测试数据。
  2.2边界值选择测试用例原则
  1)  如果输入条件规定了值的范围,则应取刚达到这个范围的边界值、以及刚超越这个范围边界的值作为测试输入数据
  2)  如果输入条件规定了值的个数,则选取最大个数、最小个数、比最大个数多一、比最小个数少一的数作为测试数据
  3)  根据规格说明的每个输出条件,使用规则1)
  4)  根据规格说明的每个输出条件,使用规则2)
  5)  若输入域是有序集合,则选取集合的第一个元素和最后一个元素作为测试用例
  6)  如果程序使用了一个内部数据结构,则应当选择内部数据结构上得边界值作为测试用例
  7)  分析规格说明,找出其他可能的边界条件
  3.错误推断法
  错误推断法一般基于以往的测试经验和直觉,参照以往的软件系统出现的错误,推测程序中可能存在的各种错误,列出程序中所有可能有的错误和容易发生错误的情况,有针对性的设计测试用例。
  例如:
  单元测试用例中列出许多在模块中常见的错误、以前产品测试中曾经发现的错误等
  输入数据为0或字符为空
  各种情况在产品说明中常常被忽视,也可能被程序员遗忘,但在实际使用中却经常发生。测试人员要站在用户的角度,考虑他们要输入的信息,而不管这些信息看起来是合法的输入还是非法的输入。

posted @ 2014-03-10 11:06 顺其自然EVO 阅读(245) | 评论 (0)编辑 收藏

PostgreSQL 数据库维护

 一、恢复磁盘空间:
  在PostgreSQL中,使用delete和update语句删除或更新的数据行并没有被实际删除,而只是在旧版本数据行的物理地址上将该行的状态置为已删除或已过期。因此当数据表中的数据变化极为频繁时,那么在一段时间之后该表所占用的空间将会变得很大,然而数据量却可能变化不大。要解决该问题,需要定期对数据变化频繁的数据表执行VACUUM操作。
  VACUUM命令存在两种形式,VACUUM和VACUUM FULL,它们之间的区别见如下表格:
  二、更新规划器统计:
  PostgreSQL查询规划器在选择最优路径时,需要参照相关数据表的统计信息用以为查询生成最合理的规划。这些统计是通过ANALYZE命令获得的,你可以直接调用该命令,或者把它当做VACUUM命令里的一个可选步骤来调用,如VACUUM ANAYLYZE table_name,该命令将会先执行VACUUM再执行ANALYZE。与回收空间(VACUUM)一样,对数据更新频繁的表保持一定频度的ANALYZE,从而使该表的统计信息始终处于相对较新的状态,这样对于基于该表的查询优化将是极为有利的。然而对于更新并不频繁的数据表,则不需要执行该操作。
  我们可以为特定的表,甚至是表中特定的字段运行ANALYZE命令,这样我们就可以根据实际情况,只对更新比较频繁的部分信息执行ANALYZE操作,这样不仅可以节省统计信息所占用的空间,也可以提高本次ANALYZE操作的执行效率。这里需要额外说明的是,ANALYZE是一项相当快的操作,即使是在数据量较大的表上也是如此,因为它使用了统计学上的随机采样的方法进行行采样,而不是把每一行数据都读取进来并进行分析。因此,可以考虑定期对整个数据库执行该命令。
  事实上,我们甚至可以通过下面的命令来调整指定字段的抽样率,如:
  ALTER TABLE testtable ALTER COLUMN test_col SET STATISTICS 200
  注意:该值的取值范围是0--1000,其中值越低采样比例就越低,分析结果的准确性也就越低,但是ANALYZE命令执行的速度却更快。如果将该值设置为-1,那么该字段的采样比率将恢复到系统当前默认的采样值,我们可以通过下面的命令获取当前系统的缺省采样值。
postgres=# show default_statistics_target;
default_statistics_target
---------------------------
100
(1 row)
  从上面的结果可以看出,该数据库的缺省采样值为100(10%)。三、VACUUM和ANALYZE的示例:
#1. 创建测试数据表。
postgres=# CREATE TABLE testtable (i integer);
CREATE TABLE
#2. 为测试表创建索引。
postgres=# CREATE INDEX testtable_idx ON testtable(i);
CREATE INDEX
#3. 创建批量插入测试数据的函数。
postgres=# CREATE OR REPLACE FUNCTION test_insert() returns integer AS $$
DECLARE
min integer;
max integer;
BEGIN
SELECT COUNT(*) INTO min from testtable;
max := min + 10000;
FOR i IN min..max LOOP
INSERT INTO testtable VALUES(i);
END LOOP;
RETURN 0;
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION
#4. 批量插入数据到测试表(执行四次)
postgres=# SELECT test_insert();
test_insert
-------------
0
(1 row)
#5. 确认四次批量插入都成功。
postgres=# SELECT COUNT(*) FROM testtable;
count
-------
40004
(1 row)
#6. 分析测试表,以便有关该表的统计信息被更新到PostgreSQL的系统表。
postgres=# ANALYZE testtable;
ANALYZE
#7. 查看测试表和索引当前占用的页面数量(通常每个页面为8k)。
postgres=# SELECT relname,relfilenode, relpages FROM pg_class WHERE relname = 'testtable' or relname = 'testtable_idx';
relname       | relfilenode    | relpages
---------------+-------------+----------
testtable        |       17601   |      157
testtable_idx  |       17604   |       90
#8. 批量删除数据。
postgres=# DELETE FROM testtable WHERE i < 30000;
DELETE 30003
#9. 执行vacuum和analyze,以便更新系统表,同时为该表和索引记录高水标记。
#10. 这里需要额外说明的是,上面删除的数据均位于数据表的前部,如果删除的是末尾部分,
#      如where i > 10000,那么在执行VACUUM ANALYZE的时候,数据表将会被物理的缩小。
postgres=# VACUUM ANALYZE testtable;
ANALYZE
#11. 查看测试表和索引在删除后,再通过VACUUM ANALYZE更新系统统计信息后的结果(保持不变)。
postgres=# SELECT relname,relfilenode, relpages FROM pg_class WHERE relname = 'testtable' or relname = 'testtable_idx';
relname      | relfilenode     | relpages
---------------+-------------+----------
testtable        |       17601   |      157
testtable_idx  |       17604   |       90
(2 rows)
#12. 再重新批量插入两次,之后在分析该表以更新其统计信息。
postgres=# SELECT test_insert(); --执行两次。
test_insert
-------------
0
(1 row)
postgres=# ANALYZE testtable;
ANALYZE
#13. 此时可以看到数据表中的页面数量仍然为之前的高水标记数量,索引页面数量的增加
#      是和其内部实现方式有关,但是在后面的插入中,索引所占的页面数量就不会继续增加。
postgres=# SELECT relname,relfilenode, relpages FROM pg_class WHERE relname = 'testtable' or relname = 'testtable_idx';
relname       | relfilenode    | relpages
---------------+-------------+----------
testtable        |       17601   |      157
testtable_idx  |       17604   |      173
(2 rows)
postgres=# SELECT test_insert();
test_insert
-------------
0
(1 row)
postgres=# ANALYZE testtable;
ANALYZE
#14. 可以看到索引的页面数量确实没有继续增加。
postgres=# SELECT relname,relfilenode, relpages FROM pg_class WHERE relname = 'testtable' or relname = 'testtable_idx';
relname      | relfilenode    | relpages
---------------+-------------+----------
testtable        |       17601   |      157
testtable_idx  |       17604   |      173
(2 rows)
#15. 重新批量删除数据。
postgres=# DELETE FROM testtable WHERE i < 30000;
DELETE 19996
#16. 从后面的查询可以看出,在执行VACUUM FULL命令之后,测试表和索引所占用的页面数量
#      确实降低了,说明它们占用的物理空间已经缩小了。
postgres=# VACUUM FULL testtable;
VACUUM
postgres=# SELECT relname,relfilenode, relpages FROM pg_class WHERE relname = 'testtable' or relname = 'testtable_idx';
relname      | relfilenode     | relpages
---------------+-------------+----------
testtable        |       17602   |      118
testtable_idx  |       17605   |       68
(2 rows)
  四、定期重建索引:
  在PostgreSQL中,为数据更新频繁的数据表定期重建索引(REINDEX INDEX)是非常有必要的。对于B-Tree索引,只有那些已经完全清空的索引页才会得到重复使用,对于那些仅部分空间可用的索引页将不会得到重用,如果一个页面中大多数索引键值都被删除,只留下很少的一部分,那么该页将不会被释放并重用。在这种极端的情况下,由于每个索引页面的利用率极低,一旦数据量显著增加,将会导致索引文件变得极为庞大,不仅降低了查询效率,而且还存在整个磁盘空间被完全填满的危险。
  对于重建后的索引还存在另外一个性能上的优势,因为在新建立的索引上,逻辑上相互连接的页面在物理上往往也是连在一起的,这样可以提高磁盘页面被连续读取的几率,从而提高整个操作的IO效率。见如下示例:
#1. 此时已经在该表中插入了大约6万条数据,下面的SQL语句将查询该索引所占用的磁盘空间。
postgres=# SELECT relname, pg_relation_size(oid)/1024 || 'K' AS size FROM pg_class WHERE relkind='i' AND relname = 'testtable_idx';
relname     | size
----------------+------
testtable_idx | 1240K
(1 row)
#2. 删除数据表中大多数的数据。
postgres=# DELETE FROM testtable WHERE i > 20000;
DELETE 50006
#3. 分析一个该表,以便于后面的SQL语句继续查看该索引占用的空间。
postgres=# ANALYZE testtable;
ANALYZE
#4. 从该查询结果可以看出,该索引所占用的空间并未减少,而是和之前的完全一样。
postgres=# SELECT pg_relation_size('testtable_idx')/1024 || 'K' AS size;
size
------
1240K
(1 row)
#5. 重建索引。
postgres=# REINDEX INDEX testtable_idx;
REINDEX
#6. 查看重建后的索引实际占用的空间,从结果中可以看出索引的尺寸已经减少。
postgres=# SELECT pg_relation_size('testtable_idx')/1024 || 'K' AS size;
size
------
368K
(1 row)
#7. 最后一点需要记住的是,在索引重建后一定要分析数据表。
postgres=# ANALYZE testtable;
ANALYZE
  五、观察磁盘使用情况:
  1. 查看数据表所占用的磁盘页面数量。
#relpages只能被VACUUM、ANALYZE和几个DDL命令更新,如CREATE INDEX。通常一个页面的长度为8K字节。
postgres=# SELECT relfilenode, relpages FROM pg_class WHERE relname = 'testtable';
relfilenode | relpages
-------------+----------
16412 |       79
(1 row)
  2. 查看指定数据表的索引名称和索引占用的磁盘页面数量。
postgres=# SELECT c2.relname, c2.relpages FROM pg_class c, pg_class c2, pg_index i
WHERE c.relname = 'testtable' AND c.oid = i.indrelid AND c2.oid = i.indexrelid
ORDER BY c2.relname;
relname    | relpages
---------------+----------
testtable_idx |       46
(1 row)

posted @ 2014-03-07 10:59 顺其自然EVO 阅读(642) | 评论 (0)编辑 收藏

PHP代码网站防范SQL注入漏洞攻击的建议

 所有的网站管理员都会关心网站的安全问题。说到安全就不得不说到SQL注入攻击(SQL Injection)。黑客通过SQL注入攻击可以拿到网站数据库的访问权限,之后他们就可以拿到网站数据库中所有的数据,恶意的黑客可以通过SQL注入功能篡改数据库中的数据甚至会把数据库中的数据毁坏掉。做为网络开发者的你对这种黑客行为恨之入骨,当然也有必要了解一下SQL注入这种功能方式的原理并学会如何通过代码来保护自己的网站数据库。今天就通过PHP和MySQL数据库为例,分享一下我所了解的SQL注入攻击和一些简单的防范措施和一些如何避免SQL注入攻击的建议。
  什么是SQL注入(SQL Injection)?
  简单来说,SQL注入是使用代码漏洞来获取网站或应用程序后台的SQL数据库中的数据,进而可以取得数据库的访问权限。比如,黑客可以利用网站代码的漏洞,使用SQL注入的方式取得一个公司网站后台数据库里所有的数据信息。拿到数据库管理员登录用户名和密码后黑客可以自由修改数据库中的内容甚至删除该数据库。SQL注入也可以用来检验一个网站或应用的安全性。SQL注入的方式有很多种,但本文将只讨论最基本的原理,我们将以PHP和MySQL为例。本文的例子很简单,如果你使用其它语言理解起来也不会有难度,重点关注SQL命令即可。
  一个简单的SQL注入攻击案例
  假如我们有一个公司网站,在网站的后台数据库中保存了所有的客户数据等重要信息。假如网站登录页面的代码中有这样一条命令来读取用户信息。
<?
$q = "SELECT `id` FROM `users` WHERE `username`= ' " .$_GET['username']. " ' AND `password`= ' " .$_GET['password']. " ' ";
?>
  现在有一个黑客想攻击你的数据库,他会尝试在此登录页面的用户名的输入框中输入以下代码:
  ' ; SHOW TABLES;
  点击登陆键,这个页面就会显示出数据库中的所有表。如果他现在使用下面这行命令:
  '; DROP TABLE [table name];
  这样他就把一张表删除了!
  当然,这只是一个很简单的例子,实际的SQL注入方法比这个要复杂得多,黑客也愿意花大量的时间来不断尝试来攻击你的代码。有一些程序软件也可以自动地来不断尝试SQL注入攻击。了解了SQL注入的攻击原理后,我们来看一下如何防范SQL注入攻击。
  防范SQL注入 - 使用mysql_real_escape_string()函数
  在数据库操作的代码中用这个函数mysql_real_escape_string()可以将代码中特殊字符过滤掉,如引号等。如下例:
<?
$q = "SELECT `id` FROM `users` WHERE `username`= ' " .mysql_real_escape_string( $_GET['username'] ). " ' AND `password`= ' " .mysql_real_escape_string( $_GET['password'] ). " ' ";
?>
  防范SQL注入 - 使用mysql_query()函数
  mysql_query()的特别是它将只执行SQL代码的第一条,而后面的并不会执行。回想在最前面的例子中,黑客通过代码来例后台执行了多条SQL命令,显示出了所有表的名称。所以mysql_query()函数可以取到进一步保护的作用。我们进一步演化刚才的代码就得到了下面的代码:
<?
//connection
$database = mysql_connect("localhost", "username","password");
//db selection
mysql_select_db("database", $database);
$q = mysql_query("SELECT `id` FROM `users` WHERE `username`= ' " .mysql_real_escape_string( $_GET['username'] ). " ' AND `password`= ' " .mysql_real_escape_string( $_GET['password'] ). " ' ", $database);
?>
  除此之外,我们还可以在PHP代码中判断输入值的长度,或者专门用一个函数来检查输入的值。所以在接受用户输入值的地方一定要做好输入内容的过滤和检查。当然学习和了解最新的SQL注入方式也非常重要,这样才能做到有目的的防范。如果使用的是平台式的网站系统如Wordpress,要注意及时打上官方的补丁或升级到新的版本。如果有讲得不对的地方或不理解的请在评论区留言。

posted @ 2014-03-07 10:58 顺其自然EVO 阅读(689) | 评论 (0)编辑 收藏

开源自动化测试框架Tellurium

  Tellurium是什么?
  Tellurium是一种自动化的web测试框架。虽然它是在selemium的阶段上建立起来的,但两者之间有许多概念上的差异,Tellurium的主要特点如下:
  · 不是单一的“记录和播放”风格。
  · 基于UI模块,也就是说,它侧重于UI元素
  · 让你有结构化的代码执行用户界面和测试代码之间的解耦
  · 鲁棒性的变化,Tellurium达到使用复合定位建立在运行时和组的定位器定位,删除里面的UI模块和外部UI元素的UI元素之间的依赖
  · 表达所使用Groovy动态语言特性和DSL
  · 可重复使用,用户界面模块可重复使用相同的应用程序和Tellurium部件,可用于不同的应用
  · 地址在网络上的动态因素。UI模板使用数据网格和Tellurium UI对象的回应属性可以处理JavaScript事件
  · 核心框架是在Groovy实现和测试,可以在Groovy中的JUnit,TestNG的,或纯DSL脚本书面
  · 支持数据驱动测试
  · 提供Maven原型
  如何使用Tellurium?
  使用Maven创建一个新的Tellurium 测试项目
  首先,你需要安装maven ,确保本机有maven环境。关于maven环境的搭建,可以参考的我的博客,关于maven的文章
  http://www.cnblogs.com/fnng/category/345480.html
  找到maven目录下的settings.xml 文件,我本机的路径在:F:\maven\apache-maven-3.0.3\conf\目录下。
  打开文件,并在<profiles>....</profiles>之间添加如下信息:
<parofiles>
<profile>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>kungfuters-public-snapshots-repo</id>
<name>Kungfuters.org Public Snapshot Repository</name>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<url>http://maven.kungfuters.org/content/repositories/snapshots</url>
</repository>
<repository>
<id>kungfuters-public-releases-repo</id>
<name>Kungfuters.org Public Releases Repository</name>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
<url>http://maven.kungfuters.org/content/repositories/releases</url>
</repository>
</repositories>
</profile>
<parofiles>

 打开的你的命令提示符,切换到你的工作空间的目录下,运行下列Maven命令来创建一个新的Tellurium测试方案(项目)"demo"
  mvn archetype:generate -DgroupId=example -DartifactId=demo -DarchetypeArtifactId=tellurium-junit-archetype -DarchetypeGroupId=tellurium -DarchetypeVersion=0.6.0
  然后切换到该项目的目录下执行:mvn  eclipse:eclipse   构建成我们的eclipse所能识别的项目结构。
  打开Eclipse将我们构建完成的项目导入。完成后项目结构如下:
  在Telluriumconfig.groovy文件中包含Tellurium项目设置,你能根据你的需求对它进行自定义设置
tellurium{
//embedded selenium server configuration
embeddedserver {
//port number
port = "4444"
//whether to use multiple windows
useMultiWindows = false
//whether to run the embedded selenium server. If false, you need to manually set up a selenium server
runInternally = true
//profile location
profile = ""
//user-extension.js file, for example, "target/test-classes/extension/user-extensions.js"
userExtension = ""
}
//event handler
eventhandler{
//whether we should check if the UI element is presented
checkElement = false
//wether we add additional events like "mouse over"
extraEvent = true
}
//data accessor
accessor{
//whether we should check if the UI element is presented
checkElement = true
}
connector{
//selenium server host
//please change the host if you run the Selenium server remotely
serverHost = "localhost"
//server port number the client needs to connect
port = "4444"
//base URL
baseUrl = "http://localhost:8080"
//Browser setting, valid options are
//  *firefox [absolute path]
//  *iexplore [absolute path]
//  *chrome
//  *iehta
browser = "*chrome"
//user's class to hold custom selenium methods associated with user-extensions.js
//should in full class name, for instance, "com.mycom.CustomSelenium"
customClass = ""
}
datadriven{
dataprovider{
//specify which data reader you like the data provider to use
//the valid options include "PipeFileReader", "CVSFileReader" at this point
reader = "PipeFileReader"
}
}
test{
//at current stage, the result report is only for tellurium data driven testing
//we may add the result report for regular tellurium test case
result{
//specify what result reporter used for the test result
//valid options include "SimpleResultReporter", "XMLResultReporter", and "StreamXMLResultReporter"
reporter = "XMLResultReporter"
//the output of the result
//valid options include "Console", "File" at this point
//if the option is "File", you need to specify the file name, other wise it will use the default
//file name "TestResults.output"
output = "Console"
//test result output file name
filename = "TestResult.output"
}
exception{
//whether Tellurium captures the screenshot when exception occurs.
//Note that the exception is the one thrown by Selenium Server
//we do not care the test logic errors here
captureScreenshot = true
//we may have a series of screenshots, specify the file name pattern here
//Here the ? will be replaced by the timestamp and you might also want to put
//file path in the file name pattern
filenamePattern = "Screenshot?.png"
}
}
uiobject{
builder{
//user can specify custom UI objects here by define the builder for each UI object
//the custom UI object builder must extend UiObjectBuilder class
//and implement the following method:
//
// public build(Map map, Closure c)
//
//For container type UI object, the builder is a bit more complicated, please
//take the TableBuilder or ListBuilder as an example
//example:
//        Icon="org.tellurium.builder.IconBuilder"
}
}
widget{
module{
//define your widget modules here, for example Dojo or ExtJs //
included="dojo, extjs"
included=""
}
}
}

 GoogleSearchModule.groovy是用户界面模块的谷歌搜索,它自化生成Tellurium 所需要的火狐浏览器插件TrUMP.  doGoogleSearch() 和 doImFeelingLucky() 两个方法是增加定期谷歌搜索和谷歌“手气不错”搜索。
public class GoogleSearchModule extends DslContext {
public void defineUi() {
ui.Container(uid: "Google", clocator: [tag: "table"]) {
InputBox(uid: "Input", clocator: [tag: "input", title: "Google Search", name: "q"])
SubmitButton(uid: "Search", clocator: [tag: "input", type: "submit", value: "Google Search", name: "btnG"])
SubmitButton(uid: "ImFeelingLucky", clocator: [tag: "input", type: "submit", value: "I'm Feeling Lucky", name: "btnI"])
}
}
public void doGoogleSearch(String input) {
keyType "Google.Input", input
pause 500
click "Google.Search"
waitForPageToLoad 30000
}
public void doImFeelingLucky(String input) {
type "Google.Input", input
pause 500
click "Google.ImFeelingLucky"
waitForPageToLoad 30000
}
}
  因为Tellurium只支持groovy语言,所以无groovy语言无法直接在Eclipse IDE中运行,需要Eclipse安装对groovy语言支持的插件。
  Groovy-Eclipse 2.5.· 插件下载地址:
  http://www.oschina.net/news/·9279/groovy-eclipse-25·
  当然,你也可以使用IntelliJ IDEA 工具,它同样也运行java语言非常优秀的IDE。 而且IntelliJ IDEA本身是支持groovy语言。
  Tellurium IDE 插件
  这个同样也是基于firefox浏览器的插件有,功能与selenium IDE类似,如果你熟悉selenium IDE的话,Tellurium IDE就很容易操作。
  Tellurium IDE 插件安装地址:
  https://addons.mozilla.org/en-US/firefox/addon/tellurium-ide/?src=search
  注意:本插件不支持最新的firefox 9 ,firefox这小子一年换版本比翻书还快,本人使用的是firefox 3.6 版本,用firefox打开上面的链接后点击“add  to  firefox”根据提示,浏览器开始下载安装重启。
  在菜单栏---工具----Tellurium IDE打开插件。
  我们打开人人网的注册页面,填写个人信息,Tellurium IDE会自动记录我的操作。
  Record :录制按钮。打开时默认是按下的,再次点击将取消录制状态。
  Step :单步运行。点击一次,运行一步。
  Run : 运行按钮。点击之后将会把脚本从头到尾运行一遍。
  Clear : 清楚脚本。清楚录制的脚本。
  本例子录制了一个人人网的注册页面(不完整,只是填写了注册信息,并被“提交”注册)。
  我们切换到Source View标签,可查看录制的代码。
  点击菜单栏File 可选择将代码以不同的形式导出或保存到剪切版上。
  在Eclipse中运行测试代码
  我们在Eclipse中创建一个NewUiModule.groovy 的文件。并把我Tellurium IDE中录制的代码插入,内容如下:
class NewUiModule extends DslContext {
public void defineUi() {
ui.Form(uid: "Regform", clocator: [tag: "form", action: "/s-c-i-reg.do", name: "regform", id: "regform", method: "post"]){
InputBox(uid: "RegEmail", clocator: [tag: "input", type: "text", class: "inputtext", id: "regEmail", name: "regEmail"])
InputBox(uid: "Pwd", clocator: [tag: "input", type: "password", class: "inputtext", id: "pwd", name: "pwd"])
InputBox(uid: "Name", clocator: [tag: "input", type: "text", class: "inputtext", id: "name", name: "name"])
RadioButton(uid: "Female", clocator: [tag: "input", type: "radio", value: "女生", id: "female", name: "gender"])
Selector(uid: "Birth_year", clocator: [tag: "select", name: "birth_year"])
Selector(uid: "Birth_month", clocator: [tag: "select", name: "birth_month"])
Selector(uid: "Birth_day", clocator: [tag: "select", name: "birth_day"])
Selector(uid: "Stage", clocator: [tag: "select", name: "stage", id: "stage"])
InputBox(uid: "Icode", clocator: [tag: "input", type: "text", class: "inputtext validate-code", id: "icode", name: "icode"])
Container(uid: "D_email", clocator: [tag: "dl", direct: "true", id: "d_email"]){
UrlLink(uid: "Xid_reg_handle", clocator: [tag: "a", text: "帐号", id: "xid_reg_handle"])
UrlLink(uid: "A", clocator: [tag: "a", text: "手机号"])
}
Container(uid: "Dl_gender", clocator: [tag: "dl", direct: "true", class: "dl_gender"]){
RadioButton(uid: "Male", clocator: [tag: "input", type: "radio", value: "男生", id: "male", name: "gender"])
}
}
connectSeleniumServer()
connectUrl "http://reg.renren.com/xn6245.do?ss=·0··3&rt=27"
type "Regform.RegEmail", "dddd"
type "Regform.RegEmail", "chongshi"
type "Regform.Pwd", "·23456"
type "Regform.Name", "小三"
click "Regform.Female"
selectByLabel "Regform.Birth_year", "80后"
selectByLabel "Regform.Birth_month", "7"
selectByLabel "Regform.Birth_day", "8"
selectByLabel "Regform.Birth_day", "7"
selectByLabel "Regform.Stage", "已经工作了"
type "Regform.Icode", "漂亮宝贝"
}  //Add your methods here
public void searchDownload(String keyword) {
keyType "TelluriumDownload.Input", keyword
click "TelluriumDownload.Search"
waitForPageToLoad 30000
}
public String[] getAllDownloadTypes() {
return getSelectOptions("TelluriumDownload.DownloadType")
}
public void selectDownloadType(String type) {
selectByLabel "TelluriumDownload.DownloadType", type
}
}

  编写一个测试类对上面的方法时行测试:
public class NewTestCase extends TelluriumJavaTestCase {
private static NewUiModule app;
@BeforeClass
public static void initUi() {
app = new NewUiModule();
app.defineUi();     }
@Before
public void setUpForTest() {
connectUrl("http://code.google.com/p/aost/downloads/list");
}
@Test
public void testTelluriumProjectPage() {
String[] allTypes = app.getAllDownloadTypes();
assertNotNull(allTypes);
assertTrue(allTypes[·].contains("All Downloads"));
app.selectDownloadType(allTypes[·]);
app.searchDownload("TrUMP");
}
}
  编译项目并运行新的测试用例.
  TestNG创建项目
  如果我们想创建一个testNG的项目,可以使用maven通过下面的命令进行创建。
  mvn archetype:generate-DgroupId=example -DartifactId=demo -DarchetypeArtifactId=tellurium-testng-archetype -DarchetypeGroupId=tellurium -DarchetypeVersion=0.6.0
  后记:
  偶然在infoq上看到了关于这个自动化测试框架的介绍,本人对于陌生的测试技术有莫大的热情,于是,开始查找它的相关资料,发现关于这个框架的资料很少。中文的更是简单的介绍。因为是中国人做的这个框架,在开源软件方面,老外嘲笑中国技术员只知道索取,没有开创精神。这使我更产生了好奇,于是花费了点时间对这个框架了解了一番。发现做的还是挺不错的,如果熟悉selenium的话,学习这个框架应该不是很难。不过这个框架也使用了一些非主流的技术,如groovy语言,我之前就没停过,可能我孤陋寡闻,由于网上关于groovy语言的资料不多。
  当然,这个框架还有很多不足,没有见有公司用这个测试框架进行测试。虽然,它的提出的一些技术是比selenium优秀的。但还需项目来验证。没有自己的官方网站,目前只寄托在google code上面。极其缺乏中文资料。看到最新的版本和新闻也是去年的,貌似今年一年都没什么动静。希望别太监了。我写这篇文档也是希望更多的测试人员来关注这个自动化测试框架。
  作者花费那么多时间和精力来做这个自动化测试框架,不管他做的如何,是否能应用我们的项目中,给我们带来利益,但他的精神是值得我们学习。

posted @ 2014-03-07 10:49 顺其自然EVO 阅读(335) | 评论 (0)编辑 收藏

基于spring与mockito单元测试Mock对象注入

1.关键词
  单元测试、spring、mockito
  2.概述
  单元测试目前已经成为项目中保证代码质量的一种必要方法,对于一些不易构造或者不易获取的对象通过mock的方式进行测试是一种很有效的处理办法。在基于spring的mock测试中,mock对象获取和使用的便利性可以提升单元测试代码的质量。
  3.实现原理
  Mock对象的注入使用注解和反射,对象注入依赖spring框架支持junit4提供的TestExcutionListeners监听器对象,在监听器中将mock对象注入到单元测试类中。
  4.新建对象方式代码
private IAccessServiceaccessService = Mockito.mock(IAccessService.class);
@BeforeClass
public static void beforeClass(){
// 构造并注入Mock对象ICmsProxyService
sceneConfigService.setAccessService(accessService);
}
  5.监听器方式代码
  5.1  实现监听器
  继承DependencyInjectionTestExecutionListener类,
  实现injectDependencies(TestContexttestContext)方法
public class MockitoDependencyInjectionTestExecutionListener extends
DependencyInjectionTestExecutionListener {
@Override
protected void injectDependencies(TestContext testContext) throws Exception {
super.injectDependencies(testContext);
init(testContext);
}
  5.2 利用反射注入mock对象
private void init(final TestContext testContext)throws Exception {
Object bean = testContext.getTestInstance();
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
Annotation[] annotations = field.getAnnotations();
for (Annotation annotation : annotations) {
if(annotationinstanceof Mock){
//注入Mock实例
MockObject obj = new MockObject();
obj.setType(field.getType());
obj.setObj(Mockito.mock(field.getType()));
field.setAccessible(true);
field.set(bean, obj.getObj());
mockObjectMap.put(field.getName(), obj);
}else if(annotation instanceofAutowired){
injectFields.add(field);
}
}
}


AutowireCapableBeanFactory factory =testContext.getApplicationContext().getAutowireCapableBeanFactory();
//Autowired注解注入mock对象
for (Field field :injectFields) {
field.setAccessible(true);
Object object = field.get(bean);
if(objectinstanceof Proxy){
Class targetClass = AopUtils.getTargetClass(object);
if(targetClass ==null)
return;
Field[] targetFields =targetClass.getDeclaredFields();
for(Field targetField : targetFields){
targetField.setAccessible(true);
if(mockObjectMap.get(targetField.getName()) ==null){
continue;
}
targetField.set(getTargetObject(object,mockObjectMap.get(targetField.getName()).getType()),mockObjectMap.get(targetField.getName()).getObj());
}
}else{
Object realObject = factory.getBean(field.getName());
if(null != realObject){
Method[] methods = realObject.getClass().getDeclaredMethods();
for (Method method : methods) {
if(method.getName().equalsIgnoreCase("set" +field.getName())){
method.invoke(realObject, mockObjectMap.get(field.getName()).getObj());
}
}
}
}
}
}
  5.3 测试类配置
  使用@TestExecutionListeners注解,引入监听器,需要mock的对象加上@Mock注解。
@TestExecutionListeners({MockitoDependencyInjectionTestExecutionListener.class})
public class AccessServiceImplTest extends BaseTestCase{
@Autowired
private IAccessServiceaccessService;
@Mock
private IPersonServicepersonService;
@Mock
private IAccessDaoaccessDao;
}
  6.总结
  监听器的方式解放了代码中硬编码注入mock对象,使得代码简洁干净。

posted @ 2014-03-07 10:39 顺其自然EVO 阅读(4989) | 评论 (0)编辑 收藏

性能测试之应用领域

1.能力验证应用领域
  在给定的条件下,系统能否具有预期的表现能力,比如某系统能否在A条件下具有B能力。有两个特点:一是要求在已确定的环境下运行(要求测试时的环境,如硬件设备、软件环境、网络环境、基础数据等已确定);二是需要根据典型场景设计测试方案和用例(需要确定相应的性能目标)<测试方法包括 性能测试、可靠性测试、压力测试、失效恢复测试>
  2.规划能力应用领域
  关注的是:应该如何使系统具有我们要求的性能能力 或是 在某种可能发生的条件下,系统具有如何的性能能力。规划能力应用领域内的问题常常会被描述为:某系统能否支持未来一段时间内的用户增长或是应该如何调整系统配置,使系统能够满足增长的用户数的需要。它具有两个特点,它是一种探索性的测试,二是它可被用于了解系统的性能以及获得拓展性能的方法 (常用的测试方法包括 负载测试、配置测试、压力测试)。
  3.性能调优应用领域
  主要对应于对系统进行调优。一般来说,性能调优活动会和其他性能测试应用领域的活动交杂在一起。由于性能调优可以调整的对象众多,而且并不要求在系统全部完成后才能进行调优。
  a>对于已经部署在生产环境中的应用系统来说,对其进行的性能调优可能首先关注应用系统部署环境的调整,如对服务器的调整、数据库参数的调整、应用服务器参数调整;
  b>对正在开发中的应用来说,性能调优会更多的关注应用逻辑的实现方法、应用中涉及的算法、数据库访问层的设计等因素,此时并不要求测试环境是实际的生产环境,只要整个调优过程中具有一个可用于比较的测试基准测试环境即可
  确定基准环境、基准负载和基准性能指标------------->调整系统允许环境和实现方法,执行测试---------------->记录测试结果,进行分析
  一个标准的性能调优过程的描述如下:
  (1).确定基准环境、基准负载和基准性能指标
  (2).调整系统运行环境和实现方法,执行测试,这是性能调优过重中的核心步骤,性能调优的目的是通过调整,提高应用系统的性能表现。对于一个应用系统来说,这种调整包括3个方面
  a>.硬件环境调整:主要是对系统运行的硬件环境进行调整,包括该表系统运行的服务器、主机设备环境(改用高性能的机器、或是调整某系服务器物理内存总量、CPU数量等)、调整网络环境(更换快速的网络设备,或是采用更高带快的组网技术)
  b>.系统测试的调整:主要是对系统运行的基础平台设置进行调整,如根据应用需要调整Unix系统核心参数、调整数据库的内存池大小、调整应用服务器的内存大小、或是采用更高版本的JVM环境等
  c>.应用级别的调整:主要是对应用实现本身进行调整,包括选用新的架构、采用新的数据访问方式或修改业务逻辑的实现方法等

posted @ 2014-03-07 10:36 顺其自然EVO 阅读(265) | 评论 (0)编辑 收藏

修改Ubuntu Windows双操作系统启动顺序

/boot/grub/grub.cfg注释中已经注明,该文件为自动生成文件,不要修改
  如果你修改了,什么时候Ubuntu自己update-grub了一下,你做的更改就无效了
  1.终端代码:
root@ubuntu:~# gedit /etc/default/grub
#修改 GRUB_DEFAULT=0 为操作系统列表中目标操作系统序号,顺序从0开始计数,比如我的 windows 序号为 4#
  2.然后:
  root@ubuntu:~# update-grub
  终端提示信息:
Generating grub.cfg ...
Found linux image: /boot/vmlinuz-3.2.0-29-generic-pae
Found initrd image: /boot/initrd.img-3.2.0-29-generic-pae
Found memtest86+ image: /memtest86+.bin
Found Microsoft Windows XP Professional on /dev/sda1
done
  3.重启
  要怎么操作,其实文件的注释中已经写的很清楚了,不用搜索。

posted @ 2014-03-06 10:41 顺其自然EVO 阅读(181) | 评论 (0)编辑 收藏

渗透测试基本流程

 渗透测试的基本流程
  渗透测试一定要按照必要的流程才能更大地提高渗透测试的成功性,所以我们应该要好好地清楚渗透测试必要的流程。我们可以把渗透测试的流程分为6个阶段,这6个阶段都是必不可少的。
  1:前期交互
  这个阶段是指与客户交流渗透测试的要求和确定和渗透团队确定攻击的范围。该阶段是收集客户的要求,并制定渗透测试的计划和对团队每个人的分配工作
  2:收集渗透目标的情报
  收集渗透目标的情报是最重要的阶段。如果收集到有用的情报资料的话,可以大大提高对渗透测试的成功性。收集渗透目标的情报一般是对目标系统的分析,扫描探测,服务查点,扫描对方漏洞,查找对方系统IP等等。有时候渗透测试者也会使用上社会工程学。渗透测试者会尽力收集目标系统的配置与安全防御以及防火墙等等。
  3:与团队交流阶段(本阶段适用于团队)
  收集好目标系统的情报后,不要急于渗透目标系统,要与渗透团队进行头脑风暴。往往一个人的力量是不够的,团队集合起交流的力量是非常强大的。因为团队里面每一个人所拥有的特点和特长都会不一样。大家交流的话,可以取长补短。所以与团队交流这个阶段可以确定更快,更容易地制定入侵目标系统的方案。
  4:漏洞分析阶段
  漏洞分析阶段要综合以上所有的阶段收集回来的情报。特别是漏洞扫描结果,服务器的配置,防火墙的使用情况情报最为重要。渗透测试者可以根据以上的情报进行开发渗透代码。渗透测试者会找出目标系统的安全漏洞和挖掘系统拥有的未知漏洞进行渗透。漏洞分析阶段是进行攻击的重要阶段。
  5:渗透攻击阶段
  来到渗透攻击的阶段渗透测试者就要利用找到的目标系统漏洞进行渗透入侵,从而得到管理权限。渗透攻击的代码可以利用公开的渠道获取渗透代码,也可以由渗透测试者马上开发针对目标系统的渗透代码。如果是黑盒测试的话,渗透攻击的难度就会加多许多。黑盒测试要考虑到目标系统的检测机制,和要防止被目标系统的应急响应团队的追踪。
  6:报告阶段
  渗透测试的过程和挖掘出的安全漏洞最终都会报告给客户。渗透测试员一般都会提交渗透时发现目标系统的不足,安全漏洞,配置的问题,防火墙的问题等等。以及渗透测试员会帮助他们进行对安全漏洞的修复,和其他问题的建议与帮助等等。

posted @ 2014-03-06 10:32 顺其自然EVO 阅读(1527) | 评论 (0)编辑 收藏

1688额度中心并发交易场景下锁机制问题

 BUG标题:
  事务处理过程中数据加锁不合理,导致同一数据源在交易过程中被其他交易改变
  BUG影响:
  1688极速到账、账期支付等场景下并发下单失败或金额占用不准确现象,造成担保方金额亏损
  BUG发现过程:
  1)前期参与开发代码review,梳理1688极速到账、账期支付等场景事务处理过程和锁使用机制,识别代码风险点;
  2)测试设计阶段:针对性地设计各种交易复杂场景高并发压测;
  3)测试过程中:编写数据校验脚本,在上亿条日志中检查每笔交易订单正确性,并找到必现条件;
  原因为:交易过程中要用到的数据源被其他买家与该卖家交易时改变,导致失败却没有进行回滚,必现条件如下图所示:
  BUG解决方法:
  在数据源被锁住的代码里再次添加查询剩余额度代码后再进行金额占用,并保证失败后能进行数据回滚(注:第一次查询剩余额度目的是查询需要用到几种授信源和各自的剩余金额进行数据锁定,开发认为在查询和数据锁定之间时间极短不可能发生改变,故在锁内未加入再次查询节省性能开销),同时在回归过程中评估修改影响,注意锁内操作变多带来的接口性能下降问题,经测试确实下降3MS左右幸好满足预期。
  GBA传承
  1、针对数据库锁测试时,一定要考虑并发测试场景,并确保查询和使用数据源都在锁内进行,避免数据不准确;
  2、性能压测过程中除了观察接口调用返回正确性,还需要观察产生的数据是否完全和预期相符,数据量大时建议用脚本或者编写小工具进行校验,在上亿条日志中不要放过任何一条可疑日志。
  个人感受:
  1、事务处理和数据锁相关测试,需要介入代码review,并理清各种约束条件和触发场景设计压测用例;
  2、需求评估阶段单纯的认为接口压测仅需要把各种接口串联在一起进行并发测试,只需要观察接口返回正确性就OK,差点疏忽了复杂场景下接口返回是成功了但数据可能不一致的场景;
  3、涉及金额的测试,出现概率就算再小也是大事,需要多留一份心。

posted @ 2014-03-06 10:16 顺其自然EVO 阅读(257) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 140 141 142 143 144 145 146 147 148 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜