qileilove

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

项目开发中切换部署开发、测试、生产多环境

在开发的过程中,不可避免会接触到至少三个环境的程序部署:开发、测试和生产环境。
  每个环境都使用一套数据库配置,路径配置等,如果每次都人工的干预每一个配置文件,工作会比较繁杂,且容易遗漏并且出错。
  spring3.1之后提供了profile功能,可以切换不同的自定义profile环境,唯一的缺点是和maven结合不大好,只能在web.xml中进行修改。
  方法如下:
  1、在beans.xml中定义各个环境。
<beans profile="develop">
</beans>
<beans profile="test">
</beans>
<beans profile="product">
</beans>
  每个环境如果使用了不同的配置文件(properties文件等)可以在环境中进行加载声明。
  该段代码需在文件根节点的最后一段
  如
  <beans profile="test">
  <context:property-placeholder location="classpath*:jdbc-test.properties"/>
  </beans>
  2、定义属性之外的配置,如指定数据库bean等
<beans profile="test,develop">
<bean id="authDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver">
</property>
<property name="url"
value="jdbc:oracle:thin:@208.120.102.10:1522:ora11g">
</property>
<property name="username" value="user"></property>
<property name="password" value="passwd"></property>
</bean>
</beans>
<beans profile="product">
<bean id="authDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver">
</property>
<property name="url"
value="jdbc:oracle:thin:@198.121.33.7:1521:ora10g">
</property>
<property name="username" value="user"></property>
<property name="password" value="passwd"></property>
</bean>
</beans>
此处定义了不同环境下不同的数据库链接信息
  3、web.xml中定义当前使用哪个环境
  在web.xml中操作context-param节点
  <context-param>
  <param-name>spring.profiles.active</param-name>
  <param-value>product</param-value>
  </context-param>
  部署时指定哪个环境为激活状态即可。
  如果进行junit测试可以使用
  @ActiveProfiles({"test","develop"})
  附: 如果spring的profile可以和maven的发布共同作用就更好了,但是笔者目前还未能成功将2者结合。
  配置提醒:
  <beans xmlns="http://www.springframework.org/schema/beans" profile="test,develop" -----设置这个之后,数据库只对test,develop有效
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
  <!-- 定义数据连接池 -->
  <!-- 使用spring自带的DriverManagerDataSource方式 -->

posted @ 2014-06-13 09:56 顺其自然EVO 阅读(563) | 评论 (1)编辑 收藏

Sql Server中REPLACE函数的使用

  在sql的使用过程当中,我们偶尔会遇到这样一种情况,就是需要改变数据的存储形式,比如数据库某一张表(Info)当中有一个字段Educational(学历),以前存储的是Json数组,现在由于需求的改变,我需要将数据的存储形式改成Json格式,这样我们就需要对数据进行替换,当数据量太大时,人工操作明显是不可取的,所以作者就找到了sql当中的REPLACE函数,下面分享一下我的个人体会。
  REPLACE
  用第三个表达式替换第一个字符串表达式中出现的所有第二个给定字符串表达式。
  语法
  REPLACE ( ''string_replace1'' , ''string_replace2'' , ''string_replace3'' )
  参数
  ''string_replace1''
  待搜索的字符串表达式。string_replace1 可以是字符数据或二进制数据。
  ''string_replace2''
  待查找的字符串表达式。string_replace2 可以是字符数据或二进制数据。
  ''string_replace3''
  替换用的字符串表达式。string_replace3 可以是字符数据或二进制数据。
  返回类型
  如果 string_replace(1、2 或 3)是支持的字符数据类型之一,则返回字符数据。
  如果 string_replace(1、2 或 3)是支持的 binary 数据类型之一,则返回二进制数据。
  示例
  下例用 xxx 替换 abcdefghi 中的字符串 cde。
  SELECT REPLACE(''abcdefghicde'',''cde'',''xxx'')GO
  下面是结果集:
  ------------abxxxfghixxx(1 row(s) affected)
  那么如何直接对数据进行修改呢?
  首页,我们要实现的是对表中存储的数据进行修改,那么一定会有 Update,其次,我们需要把我们的数据进行替换,那么一定会有 REPLACE。
  得出的sql语句如下:
  update [Info] set [Educational] =(select REPLACE(REPLACE([Educational],'[',''),']',''))
  执行以上的Sql语句,就会把表中存储的数据全部从Json数组变为Json字符串了。

posted @ 2014-06-12 13:07 顺其自然EVO 阅读(194) | 评论 (0)编辑 收藏

Java transient关键字使用小记

   3. transient使用细节——被transient关键字修饰的变量真的不能被序列化吗?
  思考下面的例子:
import java.io.Externalizable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
/**
* @descripiton Externalizable接口的使用
*
* @author Alexia
* @date 2013-10-15
*
*/
public class ExternalizableTest implements Externalizable {
private transient String content = "是的,我将会被序列化,不管我是否被transient关键字修饰";
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(content);
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
content = (String) in.readObject();
}
public static void main(String[] args) throws Exception {
ExternalizableTest et = new ExternalizableTest();
ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
new File("test")));
out.writeObject(et);
ObjectInput in = new ObjectInputStream(new FileInputStream(new File(
"test")));
et = (ExternalizableTest) in.readObject();
System.out.println(et.content);
out.close();
in.close();
}
}
  content变量会被序列化吗?好吧,我把答案都输出来了,是的,运行结果就是:
  是的,我将会被序列化,不管我是否被transient关键字修饰
  这是为什么呢,不是说类的变量被transient关键字修饰以后将不能序列化了吗?
  我们知道在Java中,对象的序列化可以通过实现两种接口来实现,若实现的是Serializable接口,则所有的序列化将会自动进行,若实现的是Externalizable接口,则没有任何东西可以自动序列化,需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。因此第二个例子输出的是变量content初始化的内容,而不是null。
 3. transient使用细节——被transient关键字修饰的变量真的不能被序列化吗?
  思考下面的例子:
import java.io.Externalizable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
/**
* @descripiton Externalizable接口的使用
*
* @author Alexia
* @date 2013-10-15
*
*/
public class ExternalizableTest implements Externalizable {
private transient String content = "是的,我将会被序列化,不管我是否被transient关键字修饰";
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(content);
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
content = (String) in.readObject();
}
public static void main(String[] args) throws Exception {
ExternalizableTest et = new ExternalizableTest();
ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
new File("test")));
out.writeObject(et);
ObjectInput in = new ObjectInputStream(new FileInputStream(new File(
"test")));
et = (ExternalizableTest) in.readObject();
System.out.println(et.content);
out.close();
in.close();
}
}
  content变量会被序列化吗?好吧,我把答案都输出来了,是的,运行结果就是:
  是的,我将会被序列化,不管我是否被transient关键字修饰
  这是为什么呢,不是说类的变量被transient关键字修饰以后将不能序列化了吗?
  我们知道在Java中,对象的序列化可以通过实现两种接口来实现,若实现的是Serializable接口,则所有的序列化将会自动进行,若实现的是Externalizable接口,则没有任何东西可以自动序列化,需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。因此第二个例子输出的是变量content初始化的内容,而不是null。

posted @ 2014-06-12 13:06 顺其自然EVO 阅读(203) | 评论 (0)编辑 收藏

分布式测试执行

  1 相关说明
  1.1 背景简介
  随着一个产品的自动化工作不断深入,自动化的case积累数量持续增长,绝大部分毫无依赖关系的case由于串行运行,测试执行时间达到小时界别,且不易于优化。另外,ci运行时所需机器资源的抢占互斥,运行机器的不稳定等问题也逐渐扩大。
  Hadoop分布式测试执行方案正是为了解决以上问题而产生,通过分布式执行,可以达到并行运行,提高执行效率的目的;另外,hadoop提供调度,重试等机制功能,可以提供给用户一个相对透明的计算资源池,减少用户对机器运行环境的依赖。
  1.2 分布式平台的选择
  本方案采用hadoop来作为分布式平台。首先是Hadoop是一个开源项目,有非常好的技术支持,二就是hadoop有成熟的分布式调度算法,可以很好的利用每台机器的cpu和内存资源,达到计算资源最优分配,三就是hadoop程序易于编写,便于维护。
  1.3 名词解释
  :apache基金会的开源分布式框架。
  Mapreduce :hadoop的计算模型,由map任务和reduce任务组成。
  Jobtracker  :hadoop计算系统的总控。
  Tasktracker  :hadoop计算系统的子节点。
  Slot(槽位) :tasktracker的最小计算分配单元,一个槽位可以对应一个map任务,一个机器启动一个tasktracker,槽位的话按照机器的cpu核数来分配,一般是”核数-1”。
  <2 分布式测试执行方案
  2.1 传统的单机测试执行流程
  一般的单机测试流程分为5步,如下图所示:
  1、lib库安装。包括测试框架的lib库安装以及基于该测试框架的产品业务层lib。
  2、测试环境安装。主要指被测对象的测试环境安装,包括数据库安装,server端安装等。
  3、case下载。从svn或者case库获取需要执行的case。
  4、case运行。
  5、发送报告。
  单机测试执行的优点在于逻辑简单,易于实现,缺点就是case要串行执行,无法有效里有机器的cpu和内存资源。举个例子,现在有一个8核16G的测试机,每个case的平均cpu使用率为10%,内存消耗1G,在这样的情况,一般可以做到至少6个case并行化,优化效率是不言而喻的。
  2.2 从单机测试到分布式测试执行的逻辑
  有了以上的五个步骤及相关分析,我们考虑其中可以并行执行来进行优化的就是测试执行这块了,其他lib库安装,测试环境安装等都基本是最小单元,不易切分了。
  所以从单机到分布式主要是Case执行集合的一个拆分。所以简单说,单机和分布式的区别就是case输入集合有变“而已,其他单机的测试执行过程基本不变。对于测试工程师来说,这个过程是透明的,只是执行case的环境从单机切换到多机。
  下图简要的表示了case从单机到多机的变化(6位的数字是caseid)。
  2.3 分布式运行逻辑
  这里的逻辑主要是两块,一部分是本地部分,一部分是分布式节点机器部分。我们将分布式测试执行过程封装到一个hadoop job里。
  本地部分:
  1、获取计算资源。这里的计算资源指可用的tasktracker的槽位数,这个槽位是case切分的分母。
  2、根据计算资源生成case列表。有了槽位数,最简单的切分算法就是:每节点case数=总case数/槽位数。
  3、业务层自定义操作。例如业务层测试执行时需要的程序或者配置获取,依赖的大数据推送到hdfs等。
  4、配置hadoop的job。包括input,output,执行job所需的文件或者tar包等。这里的input就是case列表。
  5、执行测试执行job。这个实际是个hadoop job。
  6、发送报告。汇总每个节点的运行结果,并发出报告。
  每个tasktracker的map任务输入是切分后的case列表,通过这种方式将整个测试执行部分分发到每个tasktracker上。
  节点部分:
  1、准备case列表。从map的input获取。
  2、根据case列表下载case。,这里类似于本地单机版的case获取,来源仍然是SVN或者CASE库。
  3、安装lib库。同本地单机版。
  4、安装测试环境。同本地单机版。
  5、执行case。同本地单机版。
  6、推送报告。
  这里hadoop还会根据每个map任务的返回值,来进行重试运行的调度。
  从以上的描述可以看到,在hadoop集群节点机器上(tasktracker),测试执行的逻辑和单机版基本无差别,所以整个改造的过程也是比较简单的
  2.4 分布式测试集群架构设计
  整个分布式测试执行依托于一个公共的计算集群,这个计算集群由两部分组成,一部分是hadoop相关的,包括hadoop的总控,子节点的tasktracker服务。另外一部分就是公共环境,包括测试框架,公共工具例如valgrind等。前者通过jobtracker来管理,后者通过统一运维系统来管理,其功能基本就是公共环境的安装和维护。
  3 收益
  经过我们的实际项目实践,这部分的收益主要体现在如下两点:
  1、测试执行时间大幅优化。15台机器的情况,所有原测试执行时间要1-2小时的模块,优化到10分钟以内。
  2、机器资源的节省。通过公共集群的维护,保证所有机器cpu满负荷运作,避免了以往单机测试执行的cpu浪费。
  4 准入原则及发展方向
  4.1 分布式改造的准入原则
  并不是所有的测试执行都可以分布式化,在我们的实际操作过程中,总结出以下几点准入原则,供参考:
  1、空白机器可运行。通过一个总控脚本就可以做到依赖环境准备,lib库安装,测试case执行等。
  2、测试框架允许case并行。
  3、业务层case对外部不存在固定依赖,例如依赖于某个写死的目录。
  4、业务层case依赖的server端口,最好是随机的。
  5、不允许业务层去操作公共环境。
  4.2 后续可能的技术方向
  1、case按照执行时间切分。按照时间切分来替代按照case数切分。
  2、从分布式测试执行过渡到云测试服务。

posted @ 2014-06-12 13:05 顺其自然EVO 阅读(167) | 评论 (0)编辑 收藏

Maven中配置生成单元测试报告配置

 对junit单元测试的报告:
  1.  -------------------------------------------------------
  2.   T E S T S
  3.  -------------------------------------------------------
  4.  Running com.liuyan.account.mail.AccountImageServiceImplTest
  5.  ---------------------------------1990
  6.  Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.926 sec
  7.  Running com.liuyan.account.mail.AccountImageUtilTest
  8.  Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.001 sec
  9.
  10.  Results :
  12.  Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
  运行报告是junit自己的报告输出,和咱们在Eclipse运行的报告差不多。以上代表运行了3个用例,和预期效果不符的是0个,失败的用例是0个,忽略的用例数是0个。
  如果需要跳过单元测试,则可以运行如下命令
  1.  mvn package -DskipTests
  大家可能要问,为何Maven能够自己寻找我们编写的测试类呢?其实还是那句约定大于配置。Maven自动去寻找src/test/java下面的类,当此文件夹下面的类符合以下规范,那么Maven默认认为他们是单元测试用例类。
  Test*.java:任何目录下以Test为开始的类
  *Test.java: 任何目录下以Test为结尾的类
  *TestCase.java: 任何目录下以TestCase为结尾的类。
  如果想在一段时间内节省项目构建时间,暂时全部忽略单元测试。那么可以在pom.xml中配置如下
1.  <build>
2.      <plugins>
3.          <plugin>
4.              <groupId>org.apache.maven.plugins</groupId>
5.              <artifactId>maven-surefire-plugin</artifactId>
6.              <version>2.5</version>
7.              <configuration>
8.                  <skipTests>true</skipTests>
9.              </configuration>
10.          </plugin>
11.      </plugins>
12.  </build>
  等到项目完全开发完了,需要测试用例的时候将其注释掉即可。
 本个模块有两个测试用例类,如果仅仅想运行一个测试用例该怎么办。运行下面命令
  1.  test -Dtest=AccountImageServiceImplTest
  这个是指定具体运行哪个测试用例。当然需要将pom文件中忽略测试用例的配置注释掉。
  也可以测试多个测试用例
  1.  mvn test -Dtest=AccountImageServiceImplTest,AccountImageUtilTest
  也可以使用模糊匹配进行测试
  1.  mvn test -Dtest=*Test
1.  <build>
2.      <plugins>
3.          <plugin>
4.              <groupId>org.apache.maven.plugins</groupId>
5.              <artifactId>maven-surefire-plugin</artifactId>
6.              <version>2.5</version>
7.              <configuration>
8.                  <includes>
9.                      <include>**/*Test.java</include>
10.                  </includes>
11.                  <excludes>
12.                      <exclude>**/AccountImageUtilTest.java</exclude>
13.                  </excludes>
14.              </configuration>
15.          </plugin>
16.      </plugins>
17.  </build>
  includes是需要测试的类,excludes是要排除之外测试用例。可以使用模糊匹配。**用来匹配任意件路经,*匹配任意类。
  Junit的单元测试报告:
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>2.12.2</version>
<configuration>
<showSuccess>false</showSuccess>
</configuration>
</plugin>
</plugins>
</reporting>
  这个默认生成的报告是txt,要生成html的报告需要使用命令mvn surefire-report:report. 这会在target/site下面生成html的报告
  后来经测试发现,其实maven-surefire-plugin就已经可以生成txt和xml的测试结果,如果要html的报告才需要maven-surefire-report-plugin
  4.  测试报告
  基本的测试报告上面已经介绍过了,下面我们看看测试覆盖率的报告。运行如下命令
  1.  mvn cobertura:cobertura
  pom配置
<project>
...
<reporting>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>2.5.1</version>
</plugin>
</plugins>
</reporting>
...
</project>
  常用命令
  mvn cobertura:help          查看cobertura插件的帮助
  mvn cobertura:clean         清空cobertura插件运行结果
  mvn cobertura:check         运行cobertura的检查任务
  mvn cobertura:cobertura     运行cobertura的检查任务并生成报表,报表生成在target/site/cobertura目录下
  cobertura:dump-datafile     Cobertura Datafile Dump Mojo
  mvn cobertura:instrument    Instrument the compiled classes
  在target文件夹下出现了一个site目录,下面是一个静态站点,里面就是单元测试的覆盖率报告。
  详细配置还可参考:http://zhanshenny.iteye.com/blog/1440571
  5.  总结
  这次我们介绍了Maven的测试,可以运行项目的单元测试用例,并生成报告。使用者可以根据自己的需要配置测试选项以满足项目的测试需求。最后说一下,测试十分重要,往往大手笔的产品测试人员和开发人员的比例是2:1。

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

UAT测试后上线出现问题的引起的思考

最近公司有一个外部项目上线了,虽然我没有参与这个项目,仅仅只是作为一个旁观者,但是关于用户的UAT测试的问题,不得表达下我的看法,
  在上线之前进行了近一个月的UAT测试,测试完成后进入了正式上线阶段。但是在正式上线后还是出现了几个让人感觉不可思议的问题。
  首先是居然出现大面积的用户电脑安装系统不成功的问题,安装成功后又出现了由于SSO的登录不成功的问题。关于这2个问题,我觉得不应该是
  在后期还会出现的问题,首先应该在需求确认和可行性分析阶段就应该验证的问题了。即使前期都没有发现,也应该在UAT给暴露出来,但是居然在上线后才暴露出来。不得不令人深思这其中的原因。
  原因不外乎下面几个:
  1.项目团队前期并没有将用户的环境纳入考虑范围,或者没有应对过这种问题。
  2.设计系统时没有进行全面而认真的分析,将问题想象的过于简单了,结果导致上线后用SSO登录其中的几个系统都出现了问题。
  3.单元测试,集成测试,UAT测试时都没有将这2个问题真正纳入测试项。
  4.项目团队的每个人都很忙,都在疲于应付各自任务,根本没有时间去思考这些看上去微不足到却极为致命的问题。
  5.此外项目的沟通,需求确认及需求调研也存在问题,没有考虑周全可能出现意外,盲目乐观,而且没有任何紧急预案,也没有给自己留下一个过渡的缓冲。
  综合考虑以上问题,虽然这个项目自己没有参与,但是觉得小结下还是有必要的,自己以后做项目也要引以为戒。

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

Java发邮件:Java Mail与Apache Mail

一、邮件简介
  一封邮件由很多信息构成,主要的信息如下,其他的暂时不考虑,例如抄送等:
  1、收件人:收件人的邮箱地址,例如xxx@xx.com
  2、收件人姓名:大部分的邮件显示时都会显示,例如loadfate 779554589@qq.com
  3、发件人:发件人的邮箱地址
  4、发件人姓名:
  5、主题:邮件的标题
  6、内容及附件:邮件的主要内容
  二、使用Java发邮件的通用步骤
  一般的项目中没有单独的邮件服务器,一般情况下都是使用别人的服务器。
  1、设置smtp服务器:不同的邮件服务器有不同的地址,例如:smtp.qq.com表示腾讯的smtp服务器。
  2、授权:使用该服务器的帐号和密码登录该服务器。
  3、创建邮件:创建一份包含所有信息的邮件,比如发件人、收件人、内容等。
  4、设置邮件的属性:为邮件的属性添加数据。
  5、发送邮件:因为封装不同,发送的方式不一致。
  三、Java Mail与Apache Mail
  Apache Mail是对Java Mail的封装,使用起来更加的简便,逻辑层次感更好。
  使用Java Mail只需要导入一个jar包:mail.jar。
  使用Apache Mail的时候需要导入两个jar包:mail.jar、commons-email-1.3.1.jar。
  四、使用Java Mail发送邮件
1 public static void main(String[] args) throws Exception {
2         final String user = "779554589";
3         final String password = "";
4
5         String fromAddress = "779554589@qq.com";
6         String toAddress = "loadfate@163.com";
7         String subject = "邮件测试主题";
8         String content = "这是一个测试邮件<b>哈哈</b>";
9
10         //配置参数
11         Properties props = new Properties();
12         props.setProperty("mail.smtp.auth", "true");
13         props.setProperty("mail.transport.protocol", "smtp");
14         props.setProperty("mail.host", "smtp.qq.com");
15         // 方法一:使用transport对象发送邮件
16         {
17             //通过参数生成会话
18             Session session = Session.getInstance(props);
19             //启用调试模式
20             session.setDebug(true);
21             //创建一封邮件,并设置信息
22             Message message = new MimeMessage(session);
23             message.setFrom(new InternetAddress(fromAddress));
24             message.setSubject(subject);
25             message.setText(content);
26             //创建传输
27             Transport transport = session.getTransport();
28             //连接smtp服务器
29             transport.connect(user, password);
30             //发送
31             transport.sendMessage(message, new InternetAddress[] { new InternetAddress(toAddress) });
32             transport.close();
33         }
34
35
36         // 方法二:使用Transport类静态方法发送邮件
37         {
38             //生成Session时以获取授权连接
39             Session session = Session.getInstance(props, new Authenticator() {
40                 @Override
41                 protected PasswordAuthentication getPasswordAuthentication() {
42                     return new PasswordAuthentication(user, password);
43                 }
44             });
45             session.setDebug(true);
46             //创建一封邮件,并设置信息
47             Message message = new MimeMessage(session);
48             message.setSubject(subject);
49             message.setFrom(new InternetAddress(fromAddress));
50             message.setRecipient(RecipientType.TO, new InternetAddress(toAddress));
51             message.setContent(content, "text/html;charset=utf-8");
52
53             //直接发送,message通过已经授权的Session生成
54             Transport.send(message);
55         }
56     }
  五、使用Apache Mail发送邮件
1 public class ApacheMailTest {
2     // smtp服务器
3     private String hostName = "smtp.qq.com";
4     // 帐号与密码
5     private String userName = "779554589";
6     private String password = "这是个秘密";
7     // 发件人
8     private String fromAddress = "779554589@qq.com";
9     // 发件人姓名
10     private String fromName = "loadfate";
11
12     public static void main(String[] args) throws Exception {
13         // 收件人与收件人名字
14         String toAddress = "loadfate@163.com";
15         String toName = "loadfate";
16         ApacheMailTest test = new ApacheMailTest();
17         // 所有的异常都为处理,方便浏览
18
19         test.sendSimpleEmail(toAddress, toName);
20         test.sendHtmlEmail(toAddress, toName);
21         test.sendMultiPartEmail(toAddress, toName);
22         System.out.println("发送完成");
23     }
24
25     // 发送简单邮件,类似一条信息
26     public void sendSimpleEmail(String toAddress, String toName) throws Exception {
27         SimpleEmail email = new SimpleEmail();
28         email.setHostName(hostName);// 设置smtp服务器
29         email.setAuthentication(userName, password);// 设置授权信息
30         email.setCharset("utf-8");
31         email.setFrom(fromAddress, fromName, "utf-8");// 设置发件人信息
32         email.addTo(toAddress, toName, "utf-8");// 设置收件人信息
33         email.setSubject("测试主题");// 设置主题
34         email.setMsg("这是一个简单的测试!");// 设置邮件内容
35         email.send();// 发送邮件
36     }
37
38     // 发送Html内容的邮件
39     public void sendHtmlEmail(String toAddress, String toName) throws Exception {
40         HtmlEmail email = new HtmlEmail();
41         email.setHostName(hostName);
42         email.setAuthentication(userName, password);
43         email.setCharset("utf-8");
44         email.addTo(toAddress, toName, "utf-8");
45         email.setFrom(fromAddress, fromName, "utf-8");
46         email.setSubject("这是一个html邮件");
47         // 设置html内容,实际使用时可以从文本读入写好的html代码
48         email.setHtmlMsg("<div style='width:100px;height:200px;'>a</div>");
49         email.send();
50
51     }
52
53     // 发送复杂的邮件,包含附件等
54     public void sendMultiPartEmail(String toAddress, String toName) throws Exception {
55         MultiPartEmail email = null;
56         email = new MultiPartEmail();
57         email.setHostName(hostName);
58         email.setAuthentication(userName, password);
59         email.setCharset("utf-8");
60         email.addTo(toAddress, toName, "utf-8");
61         email.setFrom(fromAddress, fromName, "utf-8");
62         email.setSubject("这是有附件的邮件");
63         email.setMsg("<a href='#'>测试内容</a>");
64
65         // 为邮件添加附加内容
66         EmailAttachment attachment = new EmailAttachment();
67         attachment.setPath("D:\\邮件.txt");// 本地文件
68         // attachment.setURL(new URL("http://xxx/a.gif"));//远程文件
69         attachment.setDisposition(EmailAttachment.ATTACHMENT);
70         attachment.setDescription("描述信息");
71         // 设置附件显示名字,必须要编码,不然中文会乱码
72         attachment.setName(MimeUtility.encodeText("邮件.txt"));
73         // 将附件添加到邮件中
74         email.attach(attachment);
75         email.send();
76     }
77 }
  六、附件
  下载地址:http://pan.baidu.com/s/1qW8rcAw
  如果失效请联系LZ
  文件说明:
  1、maildemo.zip :maildemo的源代码
  2、mail.jar :Java Mail的jar包
  3、commons-email-1.3.1.jar :Apache Mail的jar包

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

Loadrunner监控服务器资源

LoadRunner 加载监听服务器的步骤如下:
  1、在 LoadRunner Controller 下,将工作面板切换到 Run状态,Available Graphs 栏 ,System Resources Graphs目录下选择 Windows Resources,双击,在右侧4个监视栏中,选择 Windows Resources-Last 60 sec双击,放大模块,右键--> Add Measurements ,在新弹出的窗口中,添加 Monitored Server Machines 。(Add Machine 时,Name 一般填机器ip),然后 ok
  2、添加完毕后,会看到监视栏中出现10个性能服务器的数据条。这时需要观察Scenario Status 的状态,如果没有 Errors 出现,那么过一会后,会看到 Windows Resources 栏出现走动的数据,表示监听成功;否则,出现了 Errors ,则说明监听不成功。点击 红色的 Errors ,查看失败的原因。
  下面是我遇到的两种错误情况:
  情况1:
Monitor name :Windows Resources. Cannot connect to machine 10.0.64.152
Reason: 拒绝访问。
Hints:
Check that your login user name appears as administrator on this machine.
Check that you have entered the correct machine name.
Check that the machine exists and that it is online (use the ping utility for this operation).
Delete the machine and add it again (entry point: CFactory::Initialize). [MsgId: MMSG-47299]
  情况2:
  Monitor name :Windows Resources. Cannot connect to machine 10.0.64.152
  Reason: 找不到网络路径。
  解决方法:
  情况1的解决方法,是在网上找的一些方法,尝试了较多,不知是哪一个或哪几个生效起作用了。(后面,会总结一个统一的解决方法)
  情况2的解决方法:启动目标服务器的 Remote Procedure Call (RPC) Locator 服务,连接成功
  小结
  LoadRunner 添加 Windows监听器需要进行的相关设置(下面都是对被监视服务器的操作):
  1、共享C盘;(一般情况下,C 盘是共享状态,无需处理)
  2、保证如下服务开启:
  a、Remote Procedure Call (RPC)
  b、Remote Procedure Call (RPC) Locator
  c、Remote Registry
  d、Workstation
  3、本地安全设置-->安装设置-->本地策略-->安全选项,“网络访问:本地账户的共享和安全模式”项选择“经典 - 本地用户以自己的身份验证”;
  4、用户需要在本地客户端访问服务器,操作方式,开始→运行→输入服务器IP地址;

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

锤子便签的Monkeyrunner 测试脚本

  MonkeyRunner可能大家已经听过无数次了,大家在网上也看过了各种关于的它的资料了,我这里就不再过多的啰嗦它的用途了,它可以对app做功能测试也可以对手机Rom做功能测试,在没有app源码的情况下monkeyrunner可以做到很好的功能测试。MonkeyRunner有一个录制脚本的工具和回放的功能,大家去下载monkeyrecody.py和monkeyplayback.py这两个脚本就可以了,这个我这里也不讲了,网上去google可以搜到很好的教程,
  下面是我对锤子便签的一个MonkeyRunner测试脚本。这里会用到点击,拖动,截图,截图对比的一些方法,基本上我们写monkeyrunner测试脚本中常调用到的方法都用到了,这里对锤子便签测试思路是这样的:先按照正常的操作使用步骤,一步步的操作下去,每操作一步都截图,操作完成之后,再来对截图进行对比并打印出对比结果在log文本里。这里用到的是坐标点的定位方法。
  #!/usr/bin/env monkeyrunner# encoding=utf-8  #导入python中自带的time模块和sys模块,脚本中都要用到它们。import timeimport sys#MonkeyRunner自带的三个apifrom com.android.monkeyrunner import MonkeyRunner ,MonkeyDevice ,MonkeyImage#这个函数时确认年月日时分秒now=time.strftime("%Y-%m-%d-%H-%M-%S")#指定我们要保存图片的位置和打印log的位置path='D:\\picture\\'logpath="D:\\log\\"#python中获取当前运行的文件的名字name=sys.argv[0].split("\\")filename=name[len(name)-1]"""
  可以尝试输入这两句语句就可以明白上面的两个python方法了。
print(name)
print(filename)
"""#新建一个log文件log=open(logpath+filename[0:-3]+"-log"+now+".txt",'w')#连接设备,两个参数分别是等待的时间(这里的时间都是秒为单位),设备的序列号。device=MonkeyRunner.waitForConnection(5,'b4726a2d')#安装锤子便签apk。参数是apk文件的位置,因为python不支持中文输入,所以在后面用了.decode('utf-8')这个方法转码。device.installPackage ('D:\\apk\\锤子便签.apk'.decode('utf-8'))#打印出操作信息到log文件里log.write("安装apk……\n")#等待2秒MonkeyRunner.sleep(2)#启动app,参数里是app的包名/活动名device.startActivity(component='com.smartisan.notes/.NotesActivity')MonkeyRunner.sleep(2)#打印操作信息log.write("启动app……\n")#截图result = device.takeSnapshot()#保存截图 result.writeToFile(path+"主页面".decode('utf-8')+now+'.png','png')#点击搜索款的位置坐标。device.touch(111,155,'DOWN_AND_UP')MonkeyRunner.sleep(2)#输入smartisan字样device.type("smartisan")#截图result1=device.takeSnapshot()#保存截图result1.writeToFile(path+"搜索框截图".decode('utf-8')+'.png','png')#移动第一个便签的位置到最后面去,参数是:一个起始点坐标,一个终点坐标,移动的时间,移动的步骤device.drag((232,235),(216,472),3,2)MonkeyRunner.sleep(3)#截图result2=device.takeSnapshot()#保存截图result2.writeToFile(path+"移动便签".decode('utf-8')+now+".png",'png')#第一个便签向右滑动device.drag((109,360),(322,360))MonkeyRunner.sleep(3)#截图result3=device.takeSnapshot()#保存截图result3.writeToFile(path+"右移动便签".decode('utf-8')+now+".png",'png')#点击最后一个便签的位置device.touch(182,583,'DOWN_AND_UP')MonkeyRunner.sleep(5)#点击发送的位置device.touch(324,73,'DOWN_AND_UP')MonkeyRunner.sleep(5)#点击发送至长微博的位置device.touch(227,789,'DOWN_AND_UP')MonkeyRunner.sleep(5)#点击生成长微博的位置device.touch(228,791,'DOWN_AND_UP')MonkeyRunner.sleep(5)#截图result4=device.takeSnapshot()#保存图片result4.writeToFile(path+"发长微博截图".decode("utf-8")+now+'.png','png')#点击下一步的位置device.touch(426,81,'DOWN_AND_UP')MonkeyRunner.sleep(3)#截图result5=device.takeSnapshot()#保存截图result5.writeToFile(path+"输入微博账号".decode("utf-8")+now+'.png','png')#点击输入微博账号和密码的几个位置,分别输入账号和密码device.touch(196,311,'DOWN_AND_UP')MonkeyRunner.sleep(3)device.type("powermo@126.com")MonkeyRunner.sleep(3)device.touch(168,378,'DOWN_AND_UP')MonkeyRunner.sleep(3)device.type("powermo1234")MonkeyRunner.sleep(3)#点击登录device.touch(237,449,'DOWN_AND_UP')MonkeyRunner.sleep(3)#截图result6=device.takeSnapshot()#保存截图result6.writeToFile(path+"登陆微博".decode("utf-8")+now+'.png','png')#下面就开始对之前的截图进行对比了#第一张截图做对比,去文件中找到我们要对比的图片resultTrue=MonkeyRunner.loadImageFromFile('D:\\picture2\\shottrue.png')log.write("主页面对比图片……\n")#判断图片相识度是否是为90%if(result.sameAs(resultTrue,0.9)):
#在命令行打印出信息
print("主页面图片对比成功")
#打印信息到log文件
log.write("主页面图片对比成功……\n")else:
#打印信息到命令行
print("主页面图片对比失败")
log.write("主页面图片对比失败……\n")#去文件中找到我们规定的图片用来对比result1True=MonkeyRunner.loadImageFromFile('D:\\picture2\\shottrue1.png')#判断图片相识度是否是为90%if(result1.sameAs(result1True,0.9)):
print("搜索框图片对比成功")
log.write("搜索框图片对比成功……\n")else:
print("搜索框图片对比失败")
log.write("搜索框图片对比失败……\n")#对移动便签图片对比result2True=MonkeyRunner.loadImageFromFile('D:\\picture2\\shottrue2.png')##判断图片相识度是否是为80%if(result2.sameAs(result2True,0.8)):
print("移动便签对比成功")
log.write("移动便签对比成功……\n")else:
print("移动便签图片对比失败")
log.write("移动便签对比失败……\n")#对移动便签图片进行对比,去文件中找我们规定的图片result3True=MonkeyRunner.loadImageFromFile('D:\\picture2\\shottrue3.png')##判断图片相识度是否是为80%if(result3.sameAs(result3True,0.8)):
print("右移便签图片对比成功")
log.write("右移便签图片对比成功……\n")else:
print("右移便签图片对比失败")
log.write("右移便签图片对比失败……\n")#对长微博图片对比result4True=MonkeyRunner.loadImageFromFile('D:\\picture2\\shottrue4.png')if(result4.sameAs(result4True,0.8)):
print("发长微博图片对比成功")
log.write("发长微博图片对比成功……\n")else:
print("发长微博图片对比失败")
log.write("发长微博图片对比失败……\n")result5True=MonkeyRunner.loadImageFromFile('D:\\picture2\\shottrue5.png')if(result5.sameAs(result5True,0.8)):
print("输入微博账号图片对比成功")
log.write("输入微博账号图片对比成功……\n")else:
print("输入微博账号图片对比失败")
log.write("输入微博账号图片对比失败……\n")result6True=MonkeyRunner.loadImageFromFile('D:\\picture2\\shottrue6.png')if(result6.sameAs(result6True,0.8)):
print("登陆微博图片对比成功")
log.write("登陆微博图片对比成功……\n")else:
print("登陆微博图片对比失败")
log.write("登陆微博图片对比失败……\n")

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

LinkedIn的软件测试生命周期

  在LinkedIn,质量是产品发布的看门人。上周,我们告诉了大家测试前沿,LinkedIn的web框架。今天,我们将带您看看我们的测试生命周期。
  如果我们按时交货,但产品有缺陷,还是证明我们没有按时交付。——Philip Crosby
  测试生命周期
  看看在LinkedIn中测试生命周期是什么样子的:
  1.需求收集
  产品团队定义了功能需求和设计者创建的线框图。在设计和产品需求文档(PRD)被创建后,一切都涉及到整个团队,包括开发人员和测试工程师。
  2.测试计划
  在生命周期的下一步是对产品或功能的所有测试,进行计划,包括:
  创建测试计划
  编写测试用例
  按高、中或低的顺序进行优先测试用例,这样他们可以基于项目的范围运行
  举行一个测试计划和测试用例评审会议,以确保沟通和充分理解测试范围
  3.功能测试
  功能测试和回归测试在测试计划完成后开始。这个测试包括:
  新功能测试:浏览器兼容性测试,使用VMWare(Firefox、Safari、Chrome、IE)。
  Bug生命周期:在JIRA提交Bug。对bugs进行修复和验证。
  回归测试:确保没有任何现功能坏了(即:不可用)。我们使用自动化测试用例组进行测试。
  A / B测试:我们一步一步来发布功能。首先发布到内部组织,然后是公司,所以我们要捕获所有边界情况。毕竟错误是固定的,我们会慢慢倾斜于我们的用户。
  4.自动化
  自动化是并行在功能测试中完成的。我们使用 Selenium with Ruby进行UI自动化和Selenium with Java进行移动版自动化。单元测试也为新功能而写。在LinkedIn,我们发布一个新特性后,100%自动化时间表就相应计算出来了。
  5.回归运行和CI
  我们创建一个持续集成(CI)在Hudson上运行,开始单元测试运行和自动化的回归测试用例集。为了一个分支通过“GO”的标准,它必须在上述所有取得成功。

6.发布分支的创建
  分支的特性确认合格后,与所有其他功能分支合并发布。执行回归测试和功能测试分支的发布,以确保分支合并之间的兼容性。
  7.部署和测试
  接着,发布分支被部署到交付的准备环境。执行完整性测试,用不同的措施来保证向后兼容的代码被推为应用程序和服务。所有发现的问题都需要修复以确保顺利部署。测试工程师进行最后一轮回归和特性测试,开发人员需要查看日志。
  8.性能测试
  我们的性能团队要进行JMeter测试,确保功能可以在工作中正常负载。这些测试在测试环境上运行,1/8th 的生产负荷。
  9.产品推进
  我们密切关注代码,使其生产方式,确保交付出去的代码具有良好的质量。这几乎是可以庆祝的事。
  10.监控
  我们的工作还没有完成。我们还需要不断地通过监控日志和实时图表,以确保一切都顺利工作。
  11.补丁(如果需要)
  功能运用生产环境中后,我们的客户服务团队、产品团队和工程师会不断地从我们的网站会从反馈工具中监控到所有客户的反馈。如果有任何问题,我们试图尽快修复它们。在大多数情况下,会在24小时内修复。这被称为hotfixing bug。
  在LinkedIn的测试工作
  我们的目标是,在LinkedIn中,发挥产品的可用性,提供最优质的产品。作为测试工程师,我可以得到一个良好的睡眠,你要知道在LinkedIn中永远不会在任何环节对质量进行妥协。
  Quality is never an accident; It is always the result of intelligent effort. -- John Ruskin
  质量从来不是偶然的,它总是聪明努力的结果。——约翰·拉斯金

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

仅列出标题
共394页: First 上一页 101 102 103 104 105 106 107 108 109 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜