qileilove

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

附加没有日志文件的数据库方法

 今天客户那边执行SQL报错,经查看是客户服务器数据库磁盘已被全部用完,日志文件达到500GB的程度,后来由于我的错误操作导致日志文件(.ldf)被删除,后来附加.mdf文件老是说没有日志文件附加不成功,后来经过一番折腾终于解决了,下面分享一下!
  阅读目录
  操作步骤
  回到顶部
  操作步骤
  1.新建同名的数据库文件
  2.暂停SQLSetver服务
  3.将原先的mdf文件,覆盖新建的数据库,删除新数据库的ldf文件
  4.重新启动SQLSetver服务 ,这时看到的数据库是这个样子的,打不开
  5.执行以下SQL语句
1 --1.设置为紧急状态
2 alter database 数据库名称 set emergency
3 --2.设置为单用户模式
4 alter database 数据库名称 set single_user
5 --3.检查并重建日志文件
6 dbcc checkdb('数据库名称',REPAIR_ALLOW_DATA_LOSS)
7 --4.第3步操作如果有错误提示,运行第4步,没有错误则跳过
8 dbcc checkdb('数据库名称',REPAIR_REBUILD)
9 --5.恢复成多用户模式
10 alter database 数据库名称 set multi_user
  或者也可以采用手动附加(本方法参考@码道程工)

posted @ 2014-06-25 14:57 顺其自然EVO 阅读(163) | 评论 (0)编辑 收藏

Java算法-冒泡排序

思想:
  冒泡排序(BubbleSort)的基本概念是:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。
  至此第一趟结束,将最大的数放到了最后。在第二趟:仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再小于第2个数),将小数放前,大数放后,一直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。如此下去,重复以上过程,直至最终完成排序。
  由于在排序过程中总是小数往前放,大数往后放,相当于气泡往上升,所以称作冒泡排序。
  用二重循环实现,外循环变量设为i,内循环变量设为j。假如有10个数需要进行排序,则外循环重复9次,内循环依次重复9,8,...,1次。每次进行比较的两个元素都是与内循环j有关的,它们可以分别用a[j]和a[j+1]标识,i的值依次为1,2,...,9,对于每一个i,j的值依次为1,2,...10-i。
  代码:
public static void main(String[] args) {
int[] i = {1001,596,403,39,23,12,9,6,5,4,3,1};
bubbleSort(i);
}
private static void show(int[] x) {
for (int i:x) {
System.out.print(i + " ");
}
System.out.println();
}
public static void bubbleSort(int[] a) {
int temp;
show(a);
for (int i = a.length - 1;i > 0;i--) {
System.out.println("----------Round " + (a.length - i) + "-----------");
for (int j = 0;j < i;j++) {
if (a[j] > a[j + 1]) {
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
show(a);
}
}
}
 结果:
1001 596 403 39 23 12 9 6 5 4 3 1
----------Round 1-----------
596 1001 403 39 23 12 9 6 5 4 3 1
596 403 1001 39 23 12 9 6 5 4 3 1
596 403 39 1001 23 12 9 6 5 4 3 1
596 403 39 23 1001 12 9 6 5 4 3 1
596 403 39 23 12 1001 9 6 5 4 3 1
596 403 39 23 12 9 1001 6 5 4 3 1
596 403 39 23 12 9 6 1001 5 4 3 1
596 403 39 23 12 9 6 5 1001 4 3 1
596 403 39 23 12 9 6 5 4 1001 3 1
596 403 39 23 12 9 6 5 4 3 1001 1
596 403 39 23 12 9 6 5 4 3 1 1001
----------Round 2-----------
403 596 39 23 12 9 6 5 4 3 1 1001
403 39 596 23 12 9 6 5 4 3 1 1001
403 39 23 596 12 9 6 5 4 3 1 1001
403 39 23 12 596 9 6 5 4 3 1 1001
403 39 23 12 9 596 6 5 4 3 1 1001
403 39 23 12 9 6 596 5 4 3 1 1001
403 39 23 12 9 6 5 596 4 3 1 1001
403 39 23 12 9 6 5 4 596 3 1 1001
403 39 23 12 9 6 5 4 3 596 1 1001
403 39 23 12 9 6 5 4 3 1 596 1001
----------Round 3-----------
39 403 23 12 9 6 5 4 3 1 596 1001
39 23 403 12 9 6 5 4 3 1 596 1001
39 23 12 403 9 6 5 4 3 1 596 1001
39 23 12 9 403 6 5 4 3 1 596 1001
39 23 12 9 6 403 5 4 3 1 596 1001
39 23 12 9 6 5 403 4 3 1 596 1001
39 23 12 9 6 5 4 403 3 1 596 1001
39 23 12 9 6 5 4 3 403 1 596 1001
39 23 12 9 6 5 4 3 1 403 596 1001
----------Round 4-----------
23 39 12 9 6 5 4 3 1 403 596 1001
23 12 39 9 6 5 4 3 1 403 596 1001
23 12 9 39 6 5 4 3 1 403 596 1001
23 12 9 6 39 5 4 3 1 403 596 1001
23 12 9 6 5 39 4 3 1 403 596 1001
23 12 9 6 5 4 39 3 1 403 596 1001
23 12 9 6 5 4 3 39 1 403 596 1001
23 12 9 6 5 4 3 1 39 403 596 1001
----------Round 5-----------
12 23 9 6 5 4 3 1 39 403 596 1001
12 9 23 6 5 4 3 1 39 403 596 1001
12 9 6 23 5 4 3 1 39 403 596 1001
12 9 6 5 23 4 3 1 39 403 596 1001
12 9 6 5 4 23 3 1 39 403 596 1001
12 9 6 5 4 3 23 1 39 403 596 1001
12 9 6 5 4 3 1 23 39 403 596 1001
----------Round 6-----------
9 12 6 5 4 3 1 23 39 403 596 1001
9 6 12 5 4 3 1 23 39 403 596 1001
9 6 5 12 4 3 1 23 39 403 596 1001
9 6 5 4 12 3 1 23 39 403 596 1001
9 6 5 4 3 12 1 23 39 403 596 1001
9 6 5 4 3 1 12 23 39 403 596 1001
----------Round 7-----------
6 9 5 4 3 1 12 23 39 403 596 1001
6 5 9 4 3 1 12 23 39 403 596 1001
6 5 4 9 3 1 12 23 39 403 596 1001
6 5 4 3 9 1 12 23 39 403 596 1001
6 5 4 3 1 9 12 23 39 403 596 1001
----------Round 8-----------
5 6 4 3 1 9 12 23 39 403 596 1001
5 4 6 3 1 9 12 23 39 403 596 1001
5 4 3 6 1 9 12 23 39 403 596 1001
5 4 3 1 6 9 12 23 39 403 596 1001
----------Round 9-----------
4 5 3 1 6 9 12 23 39 403 596 1001
4 3 5 1 6 9 12 23 39 403 596 1001
4 3 1 5 6 9 12 23 39 403 596 1001
----------Round 10-----------
3 4 1 5 6 9 12 23 39 403 596 1001
3 1 4 5 6 9 12 23 39 403 596 1001
----------Round 11-----------
1 3 4 5 6 9 12 23 39 403 596 1001

posted @ 2014-06-25 14:52 顺其自然EVO 阅读(172) | 评论 (0)编辑 收藏

JAVA线程中的生产者和消费者问题

 生产者消费者问题是研究多线程时绕不开的问题,描述的是有一块生产者和消费者共享的有界缓冲区,生产者往缓冲区放入产品,消费者从缓冲区取走产品,这个过程可以无休止的执行,不能因缓冲区满生产者放不进产品而终止,也不能因缓冲区空消费者无产品可取而终止。
  解决生产者消费者问题的方法有两种,一种是采用某种机制保持生产者和消费者之间的同步,一种是在生产者和消费者之间建立一个管道。前一种有较高的效率并且可控制性较好,比较常用,后一种由于管道缓冲区不易控制及被传输数据对象不易封装等原因,比较少用。
  同步问题的核心在于,CPU是按时间片轮询的方式执行程序,我们无法知道某一个线程是否被执行、是否被抢占、是否结束等,因此生产者完全可能当缓冲区已满的时候还在放入产品,消费者也完全可能当缓冲区为空时还在取出产品。现在同步问题的解决方法一般是采用信号或者加锁机制,即生产者线程当缓冲区已满时放弃自己的执行权,进入等待状态,并通知消费者线程执行。消费者线程当缓冲区已空时放弃自己的执行权,进入等待状态,并通知生产者线程执行。这样一来就保持了线程的同步,并避免了线程间互相等待而进入死锁状态。
  JAVA语言提供了独立于平台的线程机制,保持了write once, run anywhere的特色。同时也提供了对同步机制的良好支持。
  JAVA中一共有四种方法支持同步,其中三个是同步方法,一个是管道方法。
  ①方法wait()/notify()
  ②方法await()/signal()
  ③阻塞队列方法BlockingQueue
  ④管道方法PipedInputStream/PipedOutputStream
  一、方法wait()/notify()
  wait()和notify()是根类Object的两个方法,也就意味着所有的AVA类都具有这个两个方法,可以认为所有的对象默认都具有一个锁,虽然看不到也无法直接操作,但它是存在的。
  wait()方法表示:当缓冲区已满或空时,生产者或消费者线程停止自己的执行,放弃锁,使自己处于等待状态,让另一个线程开始执行。
  notify()方法表示:当生产者或消费者对缓冲区放入或取出一个产品时,向另一个线程发出可执行通知,同时放弃锁,使自己处于等待状态。
import java.util.LinkedList;
public class Sycn1
{
private LinkedList<Object> myList = new LinkedList<Object>();
private int MAX = 10;
public void start()
{
new Thread(new Producer()).start();
new Thread(new Consumer()).start();
}
public static void main(String[] args) throws Exception
{
Sycn1 s1 = new Sycn1();
s1.start();
}
class Producer implements Runnable
{
public void run()
{
while (true)
{
synchronized (myList)
{
try
{
while (myList.size() == MAX)
{
System.out.println("warning: it's full!");
myList.wait();
}
Object o = new Object();
if (myList.add(o))
{
System.out.println("Producer: " + o);
myList.notify();
}
}
catch (InterruptedException ie)
{
System.out.println("producer is interrupted!");
}
}
}
}
}
class Consumer implements Runnable
{
public void run()
{
while (true)
{
synchronized (myList)
{
try
{
while (myList.size() == 0)
{
System.out.println("warning: it's empty!");
myList.wait();
}
Object o = myList.removeLast();
System.out.println("Consumer: " + o);
myList.notify();
}
catch (InterruptedException ie)
{
System.out.println("consumer is interrupted!");
}
}
}
}
}
}

  二、方法await()/signal()
  在JDK5.0以后,JAVA提供了新的更加健壮的线程处理机制,包括了同步、锁定、线程池等等,可以实现更小粒度上的控制。await()和signal()就是其中用来同步的两种方法,功能基本上和wait()/notify()相同,完全可以取代它们,但是它们和新引入的锁定机制Lock直接挂钩,具有更大的灵活性。
import java.util.LinkedList;
import java.util.concurrent.locks.*;
public class Sycn2
{
private LinkedList<Object> myList = new LinkedList<Object>();
private int MAX = 10;
private final Lock lock = new ReentrantLock();
private final Condition full = lock.newCondition();
private final Condition empty = lock.newCondition();
public void start()
{
new Thread(new Producer()).start();
new Thread(new Consumer()).start();
}
public static void main(String[] args) throws Exception
{
Sycn2 s2 = new Sycn2();
s2.start();
}
class Producer implements Runnable
{
public void run()
{
while (true)
{
lock.lock();
try
{
while (myList.size() == MAX)
{
System.out.println("warning: it's full!");
full.await();
}
Object o = new Object();
if (myList.add(o))
{
System.out.println("Producer: " + o);
empty.signal();
}
}
catch (InterruptedException ie)
{
System.out.println("producer is interrupted!");
}
finally
{
lock.unlock();
}
}
}
}
class Consumer implements Runnable
{
public void run()
{
while (true)
{
lock.lock();
try
{
while (myList.size() == 0)
{
System.out.println("warning: it's empty!");
empty.await();
}
Object o = myList.removeLast();
System.out.println("Consumer: " + o);
full.signal();
}
catch (InterruptedException ie)
{
System.out.println("consumer is interrupted!");
}
finally
{
lock.unlock();
}
}
}
}
}
  三、阻塞队列方法BlockingQueue
  BlockingQueue也是JDK5.0的一部分,是一个已经在内部实现了同步的队列,实现方式采用的是第2种await()/signal()方法。它可以在生成对象时指定容量大小。它用于阻塞操作的是put()和take()方法。
  put()方法类似于上面的生产者线程,容量最大时自动阻塞。
  take()方法类似于上面的消费者线程,容量为0时自动阻塞。
import java.util.concurrent.*;
public class Sycn3
{
private LinkedBlockingQueue<Object> queue
= new LinkedBlockingQueue<Object>(10);
private int MAX = 10;
public void start()
{
new Thread(new Producer()).start();
new Thread(new Consumer()).start();
}
public static void main(String[] args) throws Exception
{
Sycn3 s3 = new Sycn3();
s3.start();
}
class Producer implements Runnable
{
public void run()
{
while (true)
{
try
{
if (queue.size() == MAX)
System.out.println("warning: it's full!");
Object o = new Object();
queue.put(o);
System.out.println("Producer: " + o);
}
catch (InterruptedException e)
{
System.out.println("producer is interrupted!");
}
}
}
}
class Consumer implements Runnable
{
public void run()
{
while (true)
{
try
{
if (queue.size() == 0)
System.out.println("warning: it's empty!");
Object o = queue.take();
System.out.println("Consumer: " + o);
}
catch (InterruptedException e)
{
System.out.println("producer is interrupted!");
}
}
}
}
}
 运行一下代码发现问题:
  warning: it's full!
  Producer: java.lang.object@4526e2a
  可能这是因为put()和System.out.println()之间没有同步造成的,但改写成如下代码仍然没有改观
class Producer implements Runnable
{
public void run()
{
while (true)
{
try
{
if (queue.size() == MAX)
System.out.println("warning: it's full!");
Object o = new Object();
queue.put(o);
System.out.println("Producer: " + o);
}
catch (InterruptedException e)
{
System.out.println("producer is interrupted!");
}
}
}
}
  真正的原因是因为当缓冲区已满,生产者在put()操作时,put()内部调用了await()方法放弃了线程的执行,然后消费者线程执行,调用take()方法,take()内部调用了signal()方法,通知生产者线程可以执行,致使在消费者的println()还没运行的情况下生产者的println()先被执行,所以有了上面的输出。run()中的synchronized其实并没有起什么作用。对于BlockingQueue大家可以放心使用,这可不是它的问题,只是在和别的对象之间的同步有问题。对于这种多重嵌套同步的问题以后再谈。
  四、管道方法PipedInputStream/PipedOutputStream
  这个类位于java.io包中,是解决同步问题的最简单的办法,一个线程将数据写入管道,另一个线程从管道读取数据,这样便构成了一种生产者/消费者的缓冲区编程模式。在下面的代码没有使用Object对象,而是简单的读写字节值,这是因为PipedInputStream/PipedOutputStream不允许传输对象,这是JAVA本身的一个bug,具体的大家可以看sun的解释:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4131126
import java.io.*;
public class Sycn4
{
private PipedOutputStream pos;
private PipedInputStream pis;
public Sycn4()
{
try
{
pos = new PipedOutputStream();
pis = new PipedInputStream(pos);
}
catch (IOException e)
{
System.out.println(e);
}
}
public void start()
{
new Producer().start();
new Consumer().start();
}
public static void main(String[] args) throws Exception
{
Sycn4 s4 = new Sycn4();
s4.start();
}
class Producer extends Thread
{
public void run()
{
try
{
while (true)
{
int b = (int) (Math.random() * 255);
System.out.println("Producer: a byte, the value is " + b);
pos.write(b);
pos.flush();
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
pos.close();
pis.close();
}
catch (IOException e)
{
System.out.println(e);
}
}
}
}
class Consumer extends Thread
{
public void run()
{
try
{
while (true)
{
int b = pis.read();
System.out.println("Consumer: a byte, the value is " +
String.valueOf(b));
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
pos.close();
pis.close();
}
catch (IOException e)
{
System.out.println(e);
}
}
}
}
}

posted @ 2014-06-25 14:50 顺其自然EVO 阅读(158) | 评论 (0)编辑 收藏

java中的构造块、静态块等说明

 一:这篇博客写的时候我在学校已经一个星期了,为什么又会想到写这le,因为这几天又在重新学下有关spring、myBatis的知识,其中在实例化sessionFactory的时候用到了静态块,虽然在学习Hibernate时也用到过,那时候没现在想的深入。所以就回过头来记载下吧。
  最近自己突然觉得做网页好没意思啊,强烈的感觉啊,现在觉得去学习android和ios很好,因为觉得做网页都是那几个框架,一成不变啊,写来写去都是这么做。看来自己在程序这方面做不久啊。
  二:先说说静态块:
  static{
  System.out.println("静态块");
  }
  这是静态块的写法。静态块优先于主方法执行,主类中的静态块优先于类中的静态块执行。静态块优先于构造块,并且只执行一次。
  构造快:
className{
{
System.out.println("构造块");
}
publicName(){
System.out.println("构造方法中");
}
}
  构造块会随着构造方法执行,构造方法执行多少次,构造块就执行多少次,构造块优先于构造方法执行

posted @ 2014-06-25 14:45 顺其自然EVO 阅读(142) | 评论 (0)编辑 收藏

如何做好网站开发项目的需求分析

一个网站项目的确立是建立在各种各样的需求上面的,这种需求往往来自于客户的实际需求或者是出于公司自身发展的需要,其中客户的实际需求也就是说这种交易性质的需求占了绝大部分。面对对网站开发拥有不同知识层面的客户,项目的负责人对用户需求的理解程度,在很大程度上决定了此类网站开发项目的成败。因此如何更好地的了解、分析、明确用户需求,并且能够准确、清晰以文档的形式表达给参与项目开发的每个成员,保证开发过程按照满足用户需求为目的正确项目开发方向进行,是每个网站开发项目管理者需要面对的问题。
  一、那些人应该参与网站开发项目的需求分析活动
  需求分析活动其实本来就是一个和客户交流,正确引导客户能够将自己的实际需求用较为适当的技术语言进行表达(或者由相关技术人员帮助表达)以明确项目目的的过程。这个过程中也同时包含了对要建立的网站基本功能和模块的确立和策划活动。所以项目小组每个成员、客户甚至是开发方的部门经理(根据项目大小而定)的参与是必要的。而项目的管理者在需求分析中的职责有如下几个方面:
  1、负责组织相关开发人员与用户一起进行需求分析。
  2、组织美术和技术骨干代表或者全部成员(与用户讨论)编写《网站功能描述书(初稿)》文档。
  3、组织相关人员对《网站功能描述书(初稿)》进行反复讨论和修改,确定《网站功能描述书》正式文档。
  4、如果用户有这方面的能力或者用户提出要求,项目管理者也可以指派项目成员参与,而由用户编写和确定《网站功能描述书》文档。
  5、如果项目比较大的话,最好能够有部门经理或者他授权的人员参与到《网站功能描述书》的确定过程中来。
  二、完整的需求调查文档记录体系
  在整个需求分析的过程中,将按照一定规范的编写需求分析的相关文档不但可以帮助项目成员将需求分析结果更加明确化,也为以后开发过程中做到了现实文本形式的备忘,并且有助于公司日后的开发项目提供有益的借鉴和模范,成为公司在项目开发中积累的符合自身特点的经验财富。
  需求分析中需要编写的文档主要是《网站功能描述书》,他基本上是整个需求分析活动的结果性文档,也是开发工程中项目成员主要可供参考的文档。为了更加清楚的描述《网站功能描述书》往往还需要编写《用户调查报告》和《市场调研报告》文档来辅助说明。各种文档最好有一定的规范和固定格式,以便增加其可阅读性和方便阅读者快速理解文档内容,相关规定将在本文后面讨论。
  三、向用户调查些什么
  在需求分析的工程中,往往有很多不明确的用户需求,这个时候项目负责人需要调查用户的实际情况,明确用户需求。一个比较理想化的用户调查活动需要用户的充分配合,而且还有可能需要对调查对象进行必要的培训。所以调查的计划安排:时间、地点、参加人员、调查内容,都需要项目负责人和用户的共同认可。调查的形式可以是:发需求调查表、开需求调查座谈会或者现场调研。调查的内容主要如下:
  1、网站当前以及日后可能出现的功能需求。
  2、客户对网站的性能(如访问速度)的要求和可靠性的要求。
  3、确定网站维护的要求。
  4、网站的实际运行环境。
  5、网站页面总体风格以及美工效果(必要的时候用户可以提供参考站点或者由公司向用户提供)。
  6、主页面和次级页面数量,是否需要多种语言版本等7、内容管理及录入任务的分配。
  8、各种页面特殊效果及其数量(js,flash等)
  9、项目完成时间及进度(可以根据合同)
  10、明确项目完成后的维护责任。
  调查结束以后,需要编写《用户调查报告》,《报告》的要点是:
  1、调查概要说明:网站项目的名称;用户单位;参与调查人员;调查开始终止的时间;调查的工作安排。
  2、调查内容说明:用户的基本情况;用户的主要业务;信息化建设现状;网站当前和将来潜在的功能需求、性能需求、可靠性需求、实际运行环境;用户对新网站的期望等。
  3、调查资料汇编:将调查得到的资料分类汇总(如调查问卷,会议记录等等)

 四、市场调研活动内容
  通过市场调研活动,清晰的分析相似网站的性能和运行情况。可以帮助项目负责人更加清楚的构想出自己开发的网站的大体架构和模样,在总结同类网站优势和缺点的同时项目开发人员可以博采众长开发出更加优秀的网站。
  但是由于实际中时间、经费、公司能力所限,市场调研覆盖的范围有一定的局限性,在调研市场同类网站的时候,应尽可能调研到所有比较出名和优秀的同类网站。应该了解同类网站的使用环境与用户的诧异点、类似点,同类产品所定义的用户详细需求(需要公司或者项目负责人有一定的关系)。市场调研的重点应该放在主要竞争对手的作品或类似网站作品的有关信息上。市场调研可以包括下列内容:
  1、市场中同类网站作品的确定。
  2、调研作品的使用范围和访问人群。
  3、调研产品的功能设计(主要模块构成,特色功能,性能情况等等)
  4、简单评价所调研的网站情况。
  调研的目的是明确并且引导用户需求。
  对市场同类产品调研结束后,应该撰写《市场调研报告》主要包括一下要点:
  1、调研概要说明:调研计划;网站项目名称、调研单位、参与调研、调研开始终止时间。
  2、调研内容说明:调研的同类网站作品名称、网址、设计公司、网站相关说明、开发背景、主要适用访问对象、功能描述、评价等项目管理者联盟
  3、可采用借鉴的调研网站的功能设计:功能描述、用户界面、性能需求、可采用的原因。
  4、不可采用借鉴的调研网站的功能设计:功能描述、用户界面、性能需求、不可采用的原因。
  5、分析同类网站作品和主要竞争对手产品的弱点和缺陷以及本公司产品在这些方面的优势。
  6、调研资料汇编:将调研得到的资料进行分类汇总。
  五、清晰的需求分析输出——《网站功能描述书》:在拥有前期公司和客户签订的合同或者是标书的约束之下,通过较为详细具体的用户调查和市场调研活动,借鉴其输出的《用户调查报告》和《市场调研报告》文档,项目负责人应该对整个需求分析活动进行认真的总结,将分析前期不明确的需求逐一明确清晰化,并输出一份详细清晰的总结性文档――《网站功能描述书(最终版)》以供作为日后项目开发过程中的依据。《网站功能描述书》必须包含以下内容:
  1、 网站功能
  2、 网站用户界面(初步)
  3、 网站运行的软硬件环境
  4、 网站系统性能定义
  5、 网站系统的软件和硬件接口
  6、 确定网站维护的要求
  7、 确定网站系统空间租赁要求
  8、 网站页面总体风格及美工效果。
  9、 主页面及次页面大概数量。
  10、管理及内容录入任务分配。
  11、各种页面特殊效果及其数量。
  12、项目完成时间及进度(根据合同)
  13、明确项目完成后的维护责任。
  综上所述,在网站项目的需求分析中主要是由项目负责人来确定对用户需求的理解程度,而用户调查和市场调研等需求分析活动的目的就是帮助项目负责人加深对用户需求的理解和对前期不明确的地方进行明确化,以便于日后在项目开发过程中作为开发成员的依据和借鉴。
  当然一次成功的需求分析不仅需要项目负责人甚至是客户等所有项目相关人员的共同努力,还和公司的能力范围有一定关系。需要说明的是本文所述的需求分析活动内容是建立在较为理想的基础上的。由于各个公司现实情况的不同,读者可以根据自身情况不一借鉴吸收利用。重要的是能构根据本公司的情况,系统的规范此类文档做好保存和收集,相信对公司以后其他网站项目的进行以及公司自身实力的增强都会有很大帮助。

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

浅谈项目需求变更管理

“项目一旦启动,变更也就随之而来。”
  变更是无法避免的,作为一个合格的项目经理,我们应该有有效的方法来管理项目变更。
  先来看一个小案例:
  A公司正在实施一个业务系统项目,该项目包括人事行政部、信息部、业务部、财务部、生产部等多个部门人员。
  系统采取外包的形式,雇佣了某知名软件B公司的10名开发人员。
  系统实施时,出现了下述情况:A公司一个系统的用户向他所认识的B公司一个项目开发人员抱怨系统软件中的一项功能问题,并且表示希望能够进行修改。于是,该开发人员就直接对系统软件进行了修改,并告诉A已经解决了该项功能问题。
  在分析案例的问题前,我们先看看,什么是项目变更。
  当项目的某些基准发生变化时,项目的质量、成本和范围等随之发生变化,为了保证项目目标实现,就必须对项目发生的各种变化采取必要的应变措施,这种行为就是项目变更。
  项目变更产生的原因是多样的。以下是一些常见原因:
  (1)项目外部环境发生变化;
  (2)项目总体设计,项目需求分析不够周密详细;
  (3)新技术的出现、设计人员提出新的设计方案或者新的实现手段;
  (4)建设单位由于业务变化、机构重组等原因造成业务流程变化。
  (5)其它原因
  我们再来仔细分析一下,上面的案例中的做法,会出现怎么样的问题
  开发人员在听到用户的口头抱怨后,就直接对系统软件进行了修改,解决用户的问题,显然是不符合流程的。下面列举三条不合理的地方:
  首先,开发人员没有书面记录用户的变更需求,可能会导致对系统软件变更的历史无法追溯;
  其次,没有认真评估用户的变更需求是否合理,这样可能会导致与项目现有的工作可能不一致,导致影响成本、进度或者项目质量;
  再次,进行变更时,没有与其他项目相关成员进行沟通,可能会导致其他项目成员的工作不一致。
  那么我们应该怎样来处理项目中出现的变更需求呢?最好的办法是建立一套正规的程序对项目的变更进行有效的控制。
  简单地说,管理变更的程序包括以下几个步骤:
  (1)识别变更:分析项目中出现的问题是否属于变更需求,区分是否为变更需求的标准就是,某项工作是否不在项目工作基准中;
  (2)评价变更对项目的影响:如果属于变更需求,进行分析,变更会对项目成本、进度、质量等因素产生哪些影响;
  (3)设计变更的备选方案:列出几种可能的变更处理方案,比如说非常紧急的变更需求马上批准,而对项目影响较少的变更可以稍后再处理;
  (4)提出变更申请:正式提出书面的变更申请需求;
  (5)征求项目干系人的意见:所有与变更有关的项目干系人(注:项目干系人指所有与项目有正面与负责利益的人之和)都应该参与项目变更;
  (6)批准或否决变更:提交相关项目管理人员,批准或者否则项目变更;
  (7)追踪变更的实施情况:变更批准后,我们需求跟踪变更的执行情况,并且要记录在案。

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

淘宝新自动化测试框架AutoRobot简要介绍

  一、自动化测试发展
  自动化技术在不断的发展,从简单的录制回放到数据驱动到关键字驱动,再到模型驱动,每一次自动化技术的发展都会带来自动化测试工具的革新,而每一次新的测试工具的诞生都会带来新的突破,新工具的出现带来了新的体验但是也不可避免的有一些缺陷,正是因为此,才推动测试框架不断的完善,强大,下图展示了基于GUI的功能自动化测试技术的发展:
  二、淘宝自动化测试框架发展
  随着自动化技术的发展,淘宝自动化测试框架也在不断的完善,从Tcommon到Automan再到现在的AutomanX,测试框架的完善带来的是自动化效率的提升,相对来说,AutomanX已经相当的完善了,但是AutomanX是一个集成化,模块化的的测试平台,需要测试人员具备一定的编码能力,并且对mvn,junit,spring等技术有一定的了解才可以进行测试脚本的编写,同时,AutomanX是基于pageModel的,在进行脚本编写时,需要先建立pageModel,增加了脚本编写的工作量,基于此,我们又开发了新的自动化测试框架AutoRobot,来完善AutomanX的这些问题。下图展示了淘宝自动化测试框架的发展概览:
  三、AutoRobot介绍
  1.功能介绍
  AutoRobot是关键字驱动的测试框架,核心基于Selenium2.0。AutoRobot针对不同能力的测试人员提供两种脚本设计方式,一种为NoCoding方式,通过选择页面元素及对应操作来完成测试步骤的设计,另外一种为传统的Coding方式,通过编写代码完成测试脚本设计,无论使用哪种方式,AutoRobot都支持自定义的关键字设计,除了框架提供的统一的关键字定义外,不同业务可以根据业务需要设计适合自己的关键字,下图展示了AutoRobot的主要功能:
2.整体架构
  AutoRobot使用Selenium进行元素交互,使用Freemaker进行操作步骤和测试代码转换,使用junit进行测试用例组织,使用chrome插件进行页面元素自动拾取,在这些基础之上AutoRobot支持各种关键字的扩展,整体的架构如下图所示:
  3.NoCoding脚本设计
  AutoRobot使用Chrome插件进行元素拾取,使用关键字定义进行元素操作,设计好的测试步骤可以转化为自然语言描述的操作步骤,可以转化为java测试方法,还可以转化为XML语言描述的测试步骤,设计好的测试步骤如下图:
  4.Coding模式脚本设计
  使用Coding模式设计脚本,可以完全不需要在WEB上进行操作,但是最好的方式是先使用NoCoding的方式设计好测试步骤,再利用AutoRobot提供的java工程下载功能,将转换为java工程的测试下载到本地,如下图
  下载完成后解压缩,使用IDE导入工程,可以看到一个完整的测试工程,如下图所示
  通过AutoRobot下载的java工程是一个完全可运行的工程,如果对自动转换的脚本需要进行修改完善,可直接再导入后的工程中进行修改编译,下图展示的是AutoRobot自动转换的脚本
 四、AutoRobot特点
  相比AutomanX,AutoRobot具有以下特点
  1.基于关键字驱动方式item.operation(value)原理,使得脚本、业务、数据分离,有效提升了系统的可扩展性
  2.通过Chrome插件的方式进行页面元素定位自动拾取,降低页面元素拾取与定位的难度
  3.使用原生selenium元素查找方式,弱化pageModel概念,无需手动建立pageModel,极大的节省了pageModel建立维护的时间,同时提高了元素定位的效率
  4.支持NoCoding与Coding并存的用例开发模式,极大提升了用例编写的灵活度和降低用例开发的难度
  5.测试用例xml化,自然语言描述化,便于用例手动重现
  6.整个系统采用Web系统 & Chrome插件 & 核心框架,秉承高内聚、低耦合理念,极大的提升了代码的可维护性和可扩展性
  7.More and more in the future...
  试用地址:http://daily.kelude.taobao.net/pf/(只支持阿里内部使用)
  有任何意见或者建议,可以直接留言,谢谢
  五、如何部署
  1. Chrome插件(Noselector.crx)安装
  地址: \\10.232.23.62\软件备份\PC自动化框架组\5_Software   或者通过云盘下载:http://yunpan.alibaba-inc.com/group/106778
  打开Chrome浏览器的扩展chrome://extensions/,拖动Noselector.crx到扩展页面即可安装,安装成功后可看到如下图标
  2. 操作步骤编辑
  元素步骤页面,待测网址中输入测试页面,进入待测页面后,先点击元素定位器输入框,再点击Chrome插件图标开启插件,然后点击元素,则元素定位方式会自动填入元素定位器输入框中,如下图所示
  AutoRobot元素定位器展示方式使用Jquery的语法,如果不喜欢抓取的方式,可以直接再元素定位器中进行编辑。
  如果要操作浏览器或者是自定义的控件类型,则无需编辑元素名称和定位器,在元素类型中选择broswer,然后在操作中选择对应的方法即可,比如登录操作被封装在了broswer中,如果需要进行登录操作,在类型中选择broswer,在操作中选择login,在参数值中输入用户名和密码,使用逗号隔开,输入完成后保存即可
  3. 下载java工程
  通过AutoRobot写自动化用例一般不需要测试人员创建测试工程,但是有时为了调试脚本,增强脚本的健壮性,可扩展性,或者为了增强自身的编码能力,也可以先使用AutoRobot编写好测试用例,然后下载测试工程,使用传统coding方式进行脚本编写。
  选择某个测试项目,点击“下载Java工程”,即可下载该项目对应的测试工程,如下图所示
  “下载Java工程”会将测试项目工程压缩后下载到本地,解压缩后,使用eclipse的导入工程,将测试工程导入eclipse,导入成功后,则可以在eclipse中进行测试脚本的编写。

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

持续集成---减少持续集成的时间

 在持续集成领域,一个产品的发布往往都有自己的过程周期(lifecycle),大体都会划分为:构建->部署->测试->发布等几个重要阶段,其中测试是发布产品前不可或缺的重要阶段,是产品质量的保证。而能让持续集成奏效,除了要求测试脚本更充分健壮,还要求测试脚本运行得更快更好。这点对于小型项目而言可能显得无关紧要,毕竟大多小项目的测试脚本不过百条,验证点不过千“点”;但对于一个大型项目而言,测试代码源文件可能成百上千,执行完所有的测试可能要等很久,而苦等之后的结果却可能是满眼的failure, 于是如果提高测试执行速度成为迫切需要解决的问题,试想把测试阶段从2小时压缩到1小时,再从1小时压缩到30分钟,每次时间压缩带来的不仅是技术人员本身的成就感,更是对整个产品发布过程体验的改善。
  那么如何加速测试的执行呢?提起速度,我们立马可能联想到“性能”调优的步骤:先进行tuning,然后找到问题的瓶颈所在,最后逐个击破。本文暂不讨论如何进行这些步骤, 而是基于C++和Java为语言案例,TestNG和Google test为测试框架,Jenkins为持续平台做分析,从以下六个层次提出提高测试执行的一般方法:
  硬件资源层次
  工欲善其事必先利其器,提高硬件(CPU、内存、磁盘等)配置是改善执行速度的“硬”方法,硬件资源的优化不应仅仅局限在单机自身的各项指标提升,在需求不断提高的情况下,可以考虑实施虚拟机、分布式集群等方式来进一步获取更优的硬件资源,当然,涉及分布式执行时,可以借助以下持续集成平台层次的“软”实施来共同作用。
  另外,在硬件资源紧张的情况下,不同项目或者不同团队可能不得不复用一套测试环境,造成可利用资源更为紧张,此时可以错开时间测试(例如A项目组测试定时在凌晨0点启动,B项目组定时在凌晨2点)以提高速度。
  语言编码实现层次
  测试代码本身也是代码,显而易见,如果代码编写时注重效率,速度上肯定有所收益。这点可能需要“纠结”于一些日常的编码细节:例如Java中Stringbuffer和Stringbuilder的比较;C++中是i++和++i的比较。这种语言层次提高效率的文章书籍很多,这里不做过多描述。在语言编码层次上最重要的不是这些语言细节,而是避免一些消费时间的测试代码设计,减少不必要的耗时操作,例如以下几点:
  (1) 冗余的日志信息,不合理的日志级别设置等
  输出日志带来的磁盘频繁访问必然让速度下降,所以在保证日志信息充足的前提下,尽量减少日志,或者只记录失败测试的日志(毕竟对于测试者而言很少去关注成功日志),可以让测试加快。
  (2) 不合理的等待
  用户执行完某个操作,必须等待某条件的发生(例如DB里面插入一条新数据)进而执行后续动作是测试中经常面对的场景,那么等待多久成为需要考虑的问题,假设用TimeUnit.MINUTES.sleep(1)等待一分钟,在10秒即可满足条件的场景下浪费的就是50秒,所以这里必须去考虑合理sleep的时间来兼顾对资源的消耗和运行速度的影响,同时在等待方式上也可以考虑是采用循环短时间条件等待或异步通知的方式去进行。
  (3) 用例的过程
  先执行完所有测试步骤,然后做对所有步骤做一次性校验,还是做完一步校验一步,这两种方式的速度在不同场景下有所不同,所以需要权衡;相类似的,对于需要获取DB连接的用例,是每条都执行获取DB连接然后释放连接,还是所有用例执行之前获取连接,所有case执行完之后释放连接也会对执行速度有所影响。
  构建测试脚本层次
  对于一个大型项目,源文件的数目庞大或依赖的dependency过多导致代码编译占用大量时间,如何提高编译代码的速度?除了使用更好的磁盘,注重代码编写时对编译速度的影响,还可以针对不同的语言采取不同的有效策略,例如针对C++, 使用make命令编译项目时,可以加上参数-j来并行编译项目。-j参数的含义可以参考下文:
  -j [jobs], --jobs[=jobs]
  指定同步运行的作业(命令)的数量。如果有一个以上-j选项,那么只有最后一个有效。如果-j选项没有参数,那么编译过程就不会限制能够同步运行的作业的数量。
  需要说明的是,编译过程可能要求特定的顺序而导致并行编译失败,如果遇到这种问题,可以先并行、后串行(去掉-j)重复执行一次以解决。
  而对于java,Maven 3 开始支持并发build,提供了以下几种常见方式:
  mvn -T 4 clean install # Builds with 4 threads
  mvn -T 1C clean install # 1 thread per cpu core
  mvn -T 1.5C clean install # 1.5 thread per cpu core
  同时使用maven管理java项目常出现时间消耗在依赖jar的下载上,此时可以检查是否有冗余失效的repository配置、较长的下载timeout时间设置、所选择repository的连接速度等,甚至在不同测试环境下可以使用 profile来管理repository来加速测试脚本构建。
 测试框架支持层次
  在测试框架支持层次上,应该充分运用框架本身提高的丰富功能来提高测试执行速度,以Java测试框架TestNG为例:
  (1) 利用timeout控制失效等待
  如果某个测试用例等待某条件的触发而陷入长时间等待,等待的时间过长往往对于用例本身而言已失效,特别是当条件永远无法满足时。因此需要控制用例执行允许的最大timeout时间。TestNG可以给test或者test suite设置 timeout时间,分别控制具体某个或一组(testing.xml配置)自动化测试用例执行的最大允许时间:
  1. @Test(timeout = 1000)
  2. testng.xml : <suite name="Module Test" parallel="none" time-out="200000">
  (2) 利用@BeforeTest、@BeforeClass等条件注解,减少无意义测试
  测试的顺利完成都需要满足很多基础条件,例如需要测试环境就绪, 如果不使用@before类标签,则当条件不具备时,仍然会执行完所有的用例,必然带来巨大的时间浪费,因此使用@before类标签可以避免无意义的测试,@before标记的方法一旦失败,后续的相应的测试不会继续进行。
  (3) 使用框架自带的多线程支持
  例如对于TestNG自身,可以在testng.xml中设置parallel参数来指定是否并发以及并发的级别:methods|tests|classes,除了测试框架自身外,软件项目管理工具也可以提供多线程支持,例如maven的测试组件maven-surefire-plugin,提供了并发参数的设置:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.16</version>
<configuration>
<parallel>methods</parallel>
<threadCount>10</threadCount>
</configuration>
</plugin>
  持续集成平台层次
  现在市场上存在不少持续集成平台,大多持续集成平台支持并发执行用例然后汇总、发布测试结果,从而最大化提高测试执行速度。而并发执行的前提是测试代码本身及测试代码的组织支持并发,如果测试本来就含有多个模块,那么直接并发运行多个模块,最后汇总结果即可。如java以testng.xml为模块,gtest以makefile为模块,所以相比较顺序执行4个模块,并发使用4个Job并发执行,那么时间压缩可以达到4倍。在实际应用中,即使在同一个模块,我们仍然面对自动化测试用例数目过多运行速度过慢的问题,此时,可以考虑将单一模块拆分成子模块,对于Java而言较简单,配置下测试套件的xml即可;而对C++而言,如果不允许直接复制粘贴原有的makefile,就需要重新设计makefile以复用, 例如将makefile中编译的test cases定义分拆到多个makefile(如下图测试模块1的makefile引用了共用的makefile并添加了自己的测试用例)中,然后并发执行多个makefile。
  为并发执行多个job, 持续集成平台必须提供必备的支持,以Jenkins为例,可以使用multijob插件来实施,配置多个测试模块同时进行.
  并发完测试后,讲所有测试结果汇总到一个地方,然后使用xunit plugin来汇总结果(如下图),它可以汇总多个文件,且支持cpptest、,gtest等输出结果格式。
  过程改进层次
  在产品的持续集成生命周期中,可以将测试拆分成两部分放在两个阶段:基本功能快速校验阶段(fast fail)和基本功能之外的全面测试阶段。如果产品在第一阶段最基本的功能都无法通过,那么部署之后进行全面测试纯属浪费时间,这个阶段的引入可以快速的校验产品是否有必要开展全面测试。这点类似与测试用例中添加了@before类标签所带来的收效,不过更宏观且阶段划分的更清晰。
  原有过程:构建阶段->部署阶段->测试阶段->发布阶段
  细化后过程:构建阶段->部署阶段->基本功能快速校验阶段->全面测试阶段—>发布阶段
  这种过程优化可以利用持续集成平台来支持,例如对于Jenkins系统,可以使用multijob插件,将基本功能快速校验和全面测试阶段分列在不同的phase即可,执行效果如下:
  结论
  通过上面由微观到宏观六个不同层次的分析可知,要加速测试用例的执行是一个系统的过程,单靠某一方面分析可能有所偏失,并不能将测试用例的执行速度发挥极致。同时,本文针对不同层次的分析也没有提供step by step的方式描述每一个细节,只是点到为止,所以读者可以根据自己采用的语言、测试框架、持续集成平台做类似更有针对性的分析,相信在综合不同层次的综合调优后,可以让持续集成实施的更快、更好。

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

用Spring实现非端到端验收测试

验收测试让交付团队超越了基本的持续集成,即验证应用程序是否为用户提供了有价值的功能。不过对于刚开始尝试部署流水线的团队来说,想要自动化验收测试,需要跨过三大门槛。
  一是实现和维护验收测试的技术门槛。理想情况下,验收测试最好可以模拟用户与应用程序的真实交互,因此如果有图形界面的话,验收测试理应通过这个界面和系统打交道。然而,直接通过GUI进行测试会遇到几个问题:界面变化速度很快、场景的准备相对复杂、拿到测试结果较难等。比如一个典型的WEB应用程序,如果通过GUI测试,那么一般需要解析HTML标签来填写参数,提交表单,最后再次通过解析来获取系统的返回值。如果测试代码中充斥着操作HTML的细节,测试的可读性就会大大下降,验收测试本身也更脆弱,在需求变更时反而会拖慢进度。
  二是交付团队工作方式的变化。在传统团队中,需求分析、开发和测试是独立而又顺序的过程。就算能形成详细的需求文档,三方对同一段文字可能都有自己的理解。结果经常出现偏差,需求分析人员抱怨开发人员没有正确理解需求文档,开发人员抱怨需求文档不清晰、抱怨测试人员故意挑刺。敏捷实践和验收测试的出现缓解了这一问题,通过预先定义验收规格,减少文字上的误解,明确了开发工作的完成标准。不过这种思维方式的转变很难一蹴而就,需要交付团队及其利益关系人共同持续努力才能成功。
  三是对组织的环境、配置管理及部署流程的挑战。当引入自动化验收测试后,对整个部署流水线的自动化程度会有更高要求。比如部署流水线应该能够自动将应用程序部署到待测试的环境中。如果应用程序依赖数据库,那么还应该能够部署数据库schema。另外一些运行时配置也需要通过脚本完成设置。这当中除了脚本准备之外,组织的环境管理也是要能跟上的。一般情况下,稍微大一些的组织都是有专门的运维团队(而非交付团队)来管理硬件设备和其配置的。因此,这个问题一般也涉及多个团队来协作解决。
  面对这三座大山和进度压力,新手团队可能会感慨“信息量略大”而止步不前。这时不妨考虑各个击破,三个问题中的工作方式转变涉及的利益干系人最多,难度也最大;环境管理问题虽然涉及不同团队,但一般还是技术部门内的问题,关起门来好商量;验收测试的实现/维护主要是技术问题,相对最简单。如果时间和资源确实有限,不妨考虑牺牲一部分验收测试的有效性,采用简单的非端到端验收测试,在自动化部署流程方面也可以做一些折中,集中力量转变工作方式。当整个工作已经进入节奏,再去改进某个具体环节时就顺利很多了。团队只要愿意迈出一小步,也能获得很大的价值。
  过渡方案:相对简单的非端到端验收测试
  如果团队的技术积累还不足,又没有足够的资源,不妨考虑简单一些的验收测试策略作为过渡方案。非端到端的验收测试是指直接调用应用程序内部的逻辑结构来驱动测试。由于测试代码和产品代码都使用同一种语言编写,可以省去比较繁琐的数据格式解析。而在准备测试数据和场景时,直接调用内部逻辑块一般也更方便。以典型的使用SpringFramework的Java WEB应用程序为例,团队可以采用和集成测试类似的基础架构来编写非端到端的验收测试。
  这里所说的集成测试的目的是验证应用程序与外部服务的连接能否正常工作。这与应用程序实现的具体功能关系不大,因此一般只加载必需的ApplicationContext。
  
图表 1 集成测试
  非端到端的验收测试可以采用和集成测试一样的测试基础架构,这样你就可以使用熟悉的测试库了,不同的是需要加载整个ApplicationContext以尽可能模拟应用程序被部署后的情况。
  
图表 2 加载整个上下文
  由于可以访问整个ApplicationContext中的任一对象,我们可以通过访问应用程序的内部组件来执行测试,比如应用层的某个Service。但需要注意的是,选取的组件离UI层越远,其模拟真实用户交互的有效性就越差,而且受内部实现变更的影响越大。如果应用程序使用spring-webmvc的3.2以上版本,推荐使用它的mvc测试库。spring-test-mvc提供了类似http请求的DSL,此时虽然测试还是基于ApplicationContext,但并不直接访问内部组件了。这个方案对于新手团队比较友善,但请注意,这仅仅是个过渡方案,因为:
  非端到端测试无法提供全面的回归测试,尤其是UI操作。在好几个项目中,我们发现仅采用非端到端测试覆盖的功能,团队不得不保留手工回归测试。如果UI上包含了大量复杂的控制逻辑甚至有业务逻辑泄漏到UI组件中,这会稀释验收测试带来的收益。

由于测试加载的ApplicationContext和Web容器加载的ApplicationContext存在差异,非端到端测试可能会漏掉一些问题。比如在非端到端测试中一次性加载了booking-servlet.xml和root.xml,使他们成为了一个整体的上下文,而实际上在Web容器中并不完全是这样,root.xml中的bean并不能访问和控制booking-servlet.xml中的bean。一个常见问题就是如果在booking-servlet.xml中需要使用占位符,而恰巧我们已经在root.xml中有一个现成的<context:placeholder/>,看起来水到渠成,而且在测试中也没有问题,但实际部署到web容器时,就会加载失败。
  非端到端的验收测试不能作为任务完成的最终标准。因为还有UI部分还没有完成。当这类验收测试通过时,我把这个任务称作“可以进入UI调试的”。
  因此,如果团队有足够的技能和资源时还是应该直接使用端到端的验收测试,尤其当应用程序提供API(比如WebService)或是采用更易于解析的数据格式与客户端交互时。比如如果应用程序提供了基于JSON的API,完全可以使用http-client来驱动测试。
  实现非端到端的验收测试
  来看看第一个验收测试,这个案例来自于著名的dddsample,为了让验收测试能够看上去高端大气上档次,我们将使用Cucumber来组织验收测试。验收场景描述的是业务员如何登记航运货件并解释了登记完成后货件的各项状态。
  
图表 3 第一个用户故事及其验收场景
  Cucumber提供了一系列的Annotation来帮助我们验收场景文本与测试代码粘连在一起。
  
图表 4实现验收测试-1
  
图表 5 实现验收测试-2
  接下来,当运行测试时,你就可以得到一份漂亮的html报告
  
图表 6 测试运行入口
 维护非端到端的验收测试
  当团队开始编写验收测试之后,一般没过多久就会发现验收测试的开发进度越来越慢,而且有时遇到测试失败,但其实应用程序并没有缺陷的情况。验收测试对代码质量的要求也很高,相比单元测试,为了要达到测试所需的起始状态,验收测试的准备工作要更复杂。而且由于需要解析应用程序返回的数据,验收测试的断言也会更加琐碎。因此,团队最好尽早开始重构验收测试,下面的建议或许有用处:
  建立最小测试数据集并且尽可能隔离测试的数据。有时团队会发现两组测试由于依赖同一批数据而产生冲突,单独执行任一组测试都能通过,但一起执行就会失败。比如在示例代码中,对于不同的货件处理事件登记场景,验收测试都会注册一个新的货件。
  隐藏断言细节。这样可以减少重复代码,并提升测试的可读性。把琐碎的解析逻辑隐藏在领域语言编写的方法中。
  例如:如果多个测试用例都会对货件的运输状态进行断言,可以把解析细节提取出来,这样可以去除重复代码,并且减少语法噪声。
  
图表 7 抽取断言
  尽可能使用已实现的功能来实现测试场景的准备。有一些步骤可能是多个测试用例都需要来准备数据的,可以把此类步骤抽取出来。这样也可以减少重复的代码,当应用程序随着需求变化时,验收测试会有更强的适应性,而且抽取出来的方法由于隐藏了技术细节,使用起来更简练。直接使用数据脚本的方案看起来很诱人,但一旦内部结构改变,数据脚本也得跟着改。
  
图表 8 抽取公共步骤
  验收测试对实践部署流水线的团队有着重要意义,也是很大的挑战。希望大家都能找到合适自己的方法。最后介绍几个有用的测试库。
  Moco,当有外部系统集成需求时,集成测试和验收测试的一大利器。在示例代码中你可以找到一处例子。
  GreenMail,如果应用程序需要发送邮件的话,它可以提供一臂之力。不过在部署流水线上的端到端验收测试中,由于一般应用程序和测试并不运行在同一台机器上,很难对邮件进行直接的断言。这时一种方案是修改应用程序的架构,把发送邮件的实现分离到一个专用的应用中去并使用消息队列集成。那么在验收测试中,我们就可以通过监听对应的消息队列来断言了。
  Awaitility,在需要对异步处理进行断言时有所帮助。

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

持续集成:从六个层次加速测试执行

持续集成领域,一个产品的发布往往都有自己的过程周期(lifecycle),大体都会划分为:构建->部署->测试->发布等几个重要阶段,其中测试是发布产品前不可或缺的重要阶段,是产品质量的保证。而能让持续集成奏效,除了要求测试脚本更充分健壮,还要求测试脚本运行得更快更好。这点对于小型项目而言可能显得无关紧要,毕竟大多小项目的测试脚本不过百条,验证点不过千“点”;但对于一个大型项目而言,测试代码源文件可能成百上千,执行完所有的测试可能要等很久,而苦等之后的结果却可能是满眼的failure, 于是如果提高测试执行速度成为迫切需要解决的问题,试想把测试阶段从2小时压缩到1小时,再从1小时压缩到30分钟,每次时间压缩带来的不仅是技术人员本身的成就感,更是对整个产品发布过程体验的改善。
  那么如何加速测试的执行呢?提起速度,我们立马可能联想到“性能”调优的步骤:先进行tuning,然后找到问题的瓶颈所在,最后逐个击破。本文暂不讨论如何进行这些步骤, 而是基于C++和Java为语言案例,TestNG和Google test为测试框架,Jenkins为持续平台做分析,从以下六个层次提出提高测试执行的一般方法:
  硬件资源层次
  工欲善其事必先利其器,提高硬件(CPU、内存、磁盘等)配置是改善执行速度的“硬”方法,硬件资源的优化不应仅仅局限在单机自身的各项指标提升,在需求不断提高的情况下,可以考虑实施虚拟机、分布式集群等方式来进一步获取更优的硬件资源,当然,涉及分布式执行时,可以借助以下持续集成平台层次的“软”实施来共同作用。
  另外,在硬件资源紧张的情况下,不同项目或者不同团队可能不得不复用一套测试环境,造成可利用资源更为紧张,此时可以错开时间测试(例如A项目组测试定时在凌晨0点启动,B项目组定时在凌晨2点)以提高速度。
  语言编码实现层次
  测试代码本身也是代码,显而易见,如果代码编写时注重效率,速度上肯定有所收益。这点可能需要“纠结”于一些日常的编码细节:例如Java中Stringbuffer和Stringbuilder的比较;C++中是i++和++i的比较。这种语言层次提高效率的文章书籍很多,这里不做过多描述。在语言编码层次上最重要的不是这些语言细节,而是避免一些消费时间的测试代码设计,减少不必要的耗时操作,例如以下几点:
  (1) 冗余的日志信息,不合理的日志级别设置等
  输出日志带来的磁盘频繁访问必然让速度下降,所以在保证日志信息充足的前提下,尽量减少日志,或者只记录失败测试的日志(毕竟对于测试者而言很少去关注成功日志),可以让测试加快。
  (2) 不合理的等待
  用户执行完某个操作,必须等待某条件的发生(例如DB里面插入一条新数据)进而执行后续动作是测试中经常面对的场景,那么等待多久成为需要考虑的问题,假设用TimeUnit.MINUTES.sleep(1)等待一分钟,在10秒即可满足条件的场景下浪费的就是50秒,所以这里必须去考虑合理sleep的时间来兼顾对资源的消耗和运行速度的影响,同时在等待方式上也可以考虑是采用循环短时间条件等待或异步通知的方式去进行。
  (3) 用例的过程
  先执行完所有测试步骤,然后做对所有步骤做一次性校验,还是做完一步校验一步,这两种方式的速度在不同场景下有所不同,所以需要权衡;相类似的,对于需要获取DB连接的用例,是每条都执行获取DB连接然后释放连接,还是所有用例执行之前获取连接,所有case执行完之后释放连接也会对执行速度有所影响。
  构建测试脚本层次
  对于一个大型项目,源文件的数目庞大或依赖的dependency过多导致代码编译占用大量时间,如何提高编译代码的速度?除了使用更好的磁盘,注重代码编写时对编译速度的影响,还可以针对不同的语言采取不同的有效策略,例如针对C++, 使用make命令编译项目时,可以加上参数-j来并行编译项目。-j参数的含义可以参考下文:
  -j [jobs], --jobs[=jobs]
  指定同步运行的作业(命令)的数量。如果有一个以上-j选项,那么只有最后一个有效。如果-j选项没有参数,那么编译过程就不会限制能够同步运行的作业的数量。
  需要说明的是,编译过程可能要求特定的顺序而导致并行编译失败,如果遇到这种问题,可以先并行、后串行(去掉-j)重复执行一次以解决。
  而对于java,Maven 3 开始支持并发build,提供了以下几种常见方式:
  mvn -T 4 clean install # Builds with 4 threads
  mvn -T 1C clean install # 1 thread per cpu core
  mvn -T 1.5C clean install # 1.5 thread per cpu core
  同时使用maven管理java项目常出现时间消耗在依赖jar的下载上,此时可以检查是否有冗余失效的repository配置、较长的下载timeout时间设置、所选择repository的连接速度等,甚至在不同测试环境下可以使用 profile来管理repository来加速测试脚本构建。

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

仅列出标题
共394页: First 上一页 95 96 97 98 99 100 101 102 103 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜