单元测试
测试的目的是尽可能多的发现软件中存在的BUG,而不是为了隐藏BUG。事实上测试有很多种类,比如:边界测试,压力测试,性能测试等。
测试的分类:
-
黑盒测试:
黑盒测试也叫功能测试,主要关注软件每个功能是否实现,并不关注软件代码是否有错误。测试人员完全不考虑程序内部的逻辑结构和内部特性。
-
白盒测试:
白盒测试与黑盒测试相反,白盒测试主要检查软件内部逻辑结构、设计等是否符合规定。
用代码来测试你的代码。
-
灰盒测试:
灰盒测试是介于黑盒和白盒之间的一种测试,既关注功能也关注内部逻辑的实现,但并没有白盒测试那么细致。需要灰盒测试的目的是因为白盒测试效率较低。
单元测试:
单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。比如一个方法是否正确,比如是否达到想要的效果等。也是最常用的一种方式。实际开发中,需要严格要求单元测试必须约束到最小颗粒(最小的业务逻辑)。
JUnit
是一个开源的Java语言的单元测试框架,专门针对Java设计,使用最广泛。JUnit是事实上的单元测试的标准框架,任何Java开发者都应当学习并使用JUnit
编写单元测试。JUnit
是给开发者提供的工具类,单元测试目前有JUnit3
、JUnit4
、JUnit5
三个版本,每个版本测试方法有微小区别,目前使用最广泛的就是JUnit4
。
编写规范
- 单元测试代码本身必须非常简单,能一下看明白,决不能再为测试代码编写测试;
- 每个单元测试应当互相独立,不依赖运行的顺序;
- 测试时不但要覆盖常用测试用例,还要特别注意测试边界条件,例如输入为
0
,null
,空字符串""
等情况。
常用注解
-
@Test
在
JUnit3
中,是通过对测试类和测试方法的命名来确定是否是测试,且所有的测试类必须继承JUnit
的测试基类。在JUnit4
中,定义一个测试方法变得简单很多,只需要在方法前加上@Test
就行了。注:测试方法必须是
public void
,即公共、无返回数据。可以抛出异常。 -
@Ignore
当暂时不运行某些测试方法\测试类,可以在方法前加上这个注解。在运行结果中,
JUnit
会统计忽略的用例数,来提醒你。但是不建议经常这么做,因为这样的坏处是,容易忘记去更新这些测试方法,导致代码不够干净,用例遗漏。使用此标注的时候不能与其它标注一起使用,如:和@Test
标注一起使用,那就没用了。 -
@BeforeClass
当运行几个有关联的用例时,可能会在数据准备或其它前期准备中执行一些相同的命令,这个时候为了让代码更清晰,更少冗余,可以将公用的部分提取出来,放在一个方法里,并为这个方法注解
@BeforeClass
。意思是在测试类里所有用例运行之前,运行一次这个方法。例如创建数据库连接、读取文件等。注:方法名必须是
public static void
,即公开、静态、无返回。这个方法只会运行一次。 -
@AfterClass
跟
@BeforeClass
对应,在测试类里所有用例运行之后,运行一次。用于处理一些测试后续工作,例如清理数据,恢复现场。注:方法必须是
public static void
,即公开、静态、无返回。这个方法只会运行一次。 -
@Before
与
@BeforeClass
的区别在于,@Before
不止运行一次,它会在每个用例运行之前都运行一次。主要用于一些独立于用例之间的准备工作。比如两个用例都需要读取数据库里的用户A信息,但第一个用例会删除这个用户A,而第二个用例需要修改用户A。那么可以用
@BeforeClass
创建数据库连接。用@Before
来插入一条用户A信息。注:必须是
public void
,不能为static
。不止运行一次,根据用例数而定。 -
@After
:与@Before
对应。 -
@Runwith
概念:测试方法、测试类、测试集、测试运行器。
测试方法:就是用
@Test
注解的一些函数。测试类是包含一个或多个测试方法的一个Test.java文件。
测试集是一个suite,可能包含多个测试类。
测试运行器则决定了用什么方式偏好去运行这些测试集/类/方法。
@Runwith
是放在测试类名之前,用来确定这个类怎么运行的。也可以不标注,会使用默认运行器。常见运行器:
@RunWith(Parameterized.class)
参数化运行器,配合@Parameters使用junit的参数化功能@RunWith(Suite.class) @SuiteClasses({ATest.class,BTest.class,CTest.class})
测试集运行器配合使用测试集功能@RunWith(JUnit4.class)
JUnit4
的默认运行器@RunWith(JUnit38ClassRunner.class)
用于兼容JUnit3.8的运行器- 其它运行器具备更多功能。例如
@RunWith(SpringJUnit4ClassRunner.class)
集成了spring的一些功能
-
@Parameters: 用于使用参数化功能。
测试的生命周期
-
**
@BeforeClass
**注解修饰的方法(该方法要用static
修饰)会在所有方法运行前被执行,且只执行一次,通常用来为后面测试方法的准备工作,如加载配置、进行数据库的连接等。父类的@BeforeClass
注解方法会在子类的@BeforeClass
注解方法执行前执行。 -
**
@Before
**注解修饰的方法会在每个测试方法执行前执行一次,父类@Before
修饰的方法会在子类@Before
修饰的方法执行前执行。 -
**
@After
**注解修饰的方法会在每个测试方法执行后执行一次,父类@After
修饰的方法会在子类@After
修饰的方法执行后执行。 -
**
@AfterClass
**注解修饰的方法(该方法要用static
修饰)会在所有方法执行结束后执行一次,且也只执行一次,通常用来对资源进行释放,比如数据库连接的关闭等,无论测试用例里的其他方法有没有抛出异常,该方法最终都会被执行。而且父类中的被@AfterClass
注解方法修饰的方法会在子类的@AfterClass
注解修饰的方法执行之后才会被执行。
jar包
导入jar包的方法:
- 在java项目中新建一个文件夹lib,并将需要导入的jar包复制到lib文件夹中;
- 选中lib文件夹中的jar包,并配置环境变量(右键选择【Build Path】→【Add to Build Path】)。
文件解析
配置文件一般通过.properties
形式和.xml
形式的文件保存。
properties文件中数据的存储是以键值对的形式存在,每一行为一条数据,只能存储字符串形式的数 据,Properties文件中值的部分任意字符都会被当做值的一部分,尤其是空格。
- 创建资源文件:
-
在工程src中新建后缀为.properties的文件
-
编写内容:
内容以
key=value
的方式呈现,=
左右两边不要有空白,key值抵拢,value后没有符号,key和value值不能使用引号,通过#
修饰代表注释。
-
解析资源文件:
针对properties文件的读取是使用JDK中的Properties类进行数据读取。
读取步骤:
-
创建Properties对象
Properties prop = new Properties();
-
加载资源文件流(有三种方式)
方式一:传统IO流
prop.load(new FileInputStream(path))
输入流加载资源文件方式二:字节码对象获取流
类名.class. getResourceAsStream(/包路径/文件名)
注:字节码对象获取流参数的规则是前必须带/
,若资源文件没有在包中,包的路径可以省略,其底层本质还是调用类加载器实现。方式三:类加载器获取流(效率最高)
类加载器是JVM内部专门设计用于加载字节码文件到内存的一个工具
步骤:
-
得到类加载器
字节码对象.getClassLoader()
字节码对象方式获取类加载器Thread.currentThread().getContextClassLoader()
线程方式获取类加载器 -
获取流
类加载器对象.getResourceAsStream(包名/文件名)
注:类加载器获取流的时候,路径最前方不需要书写
/
-
-
读取数据
prop.getProperty(key)
返回资源文件中对应key中的值字符串,若key不存在则返回null
prop.getProperty(key,默认值)
返回资源文件中对应key中的值字符串,若key不存在则返回默认值
public class MysqlPropertiesTest { // 方法一:传统IO流获取流 @Test public void testProperties1() throws FileNotFoundException, IOException { // 1.创建一个Properties的对象 Properties prop = new Properties(); // 2.通过1oad方法进行加载到内存 prop.load(new FileInputStream("src/mysql.properties")); // 3.通过getProperty(key)获取对应的value值 String ip = prop.getProperty("ip"); String port = prop.getProperty("port"); String username = prop.getProperty("username"); String password = prop.getProperty("password"); System.out.println(ip+":"+port+","+username+":"+password); } // 方法二:字节码对象获取流 @Test public void testProperties2() throws FileNotFoundException, IOException { // 1.创建一个Properties的对象 Properties prop = new Properties(); // 2.通过1oad方法进行加载到内存 InputStream rs = MysqlPropertiesTest.class.getResourceAsStream("/src/mysql.properties"); prop.load(rs); // 3.通过getProperty(key)获取对应的value值 String ip = prop.getProperty("ip"); String port = prop.getProperty("port"); String username = prop.getProperty("username"); String password = prop.getProperty("password"); System.out.println(ip+":"+port+","+username+":"+password); } // 方法三:类加载器获取流 @Test public void testProperties3() throws FileNotFoundException, IOException { // 1.创建一个Properties的对象 Properties prop = new Properties(); // 2.通过1oad方法进行加载到内存 InputStream rs = MysqlPropertiesTest.class.getResourceAsStream("src/mysql.properties"); prop.load(rs); // 3.通过getProperty(key)获取对应的value值 String ip = prop.getProperty("ip"); String port = prop.getProperty("port"); String username = prop.getProperty("username"); String password = prop.getProperty("password"); System.out.println(ip+":"+port+","+username+":"+password); } }
-