单元测试

测试的目的是尽可能多的发现软件中存在的BUG,而不是为了隐藏BUG。事实上测试有很多种类,比如:边界测试,压力测试,性能测试等。

测试的分类

  • 黑盒测试:

    黑盒测试也叫功能测试,主要关注软件每个功能是否实现,并不关注软件代码是否有错误。测试人员完全不考虑程序内部的逻辑结构和内部特性。

  • 白盒测试:

    白盒测试与黑盒测试相反,白盒测试主要检查软件内部逻辑结构、设计等是否符合规定。

    用代码来测试你的代码。

  • 灰盒测试:

    灰盒测试是介于黑盒和白盒之间的一种测试,既关注功能也关注内部逻辑的实现,但并没有白盒测试那么细致。需要灰盒测试的目的是因为白盒测试效率较低。

单元测试

单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。比如一个方法是否正确,比如是否达到想要的效果等。也是最常用的一种方式。实际开发中,需要严格要求单元测试必须约束到最小颗粒(最小的业务逻辑)。

JUnit是一个开源的Java语言的单元测试框架,专门针对Java设计,使用最广泛。JUnit是事实上的单元测试的标准框架,任何Java开发者都应当学习并使用JUnit编写单元测试。JUnit是给开发者提供的工具类,单元测试目前有JUnit3JUnit4JUnit5三个版本,每个版本测试方法有微小区别,目前使用最广泛的就是JUnit4

官网地址 官方入门文档 官方github

编写规范

  1. 单元测试代码本身必须非常简单,能一下看明白,决不能再为测试代码编写测试;
  2. 每个单元测试应当互相独立,不依赖运行的顺序;
  3. 测试时不但要覆盖常用测试用例,还要特别注意测试边界条件,例如输入为0null,空字符串""等情况。

常用注解

  • @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包的方法

  1. 在java项目中新建一个文件夹lib,并将需要导入的jar包复制到lib文件夹中;
  2. 选中lib文件夹中的jar包,并配置环境变量(右键选择【Build Path】→【Add to Build Path】)。

文件解析

配置文件一般通过.properties形式和.xml形式的文件保存。

properties文件中数据的存储是以键值对的形式存在,每一行为一条数据,只能存储字符串形式的数 ,Properties文件中值的部分任意字符都会被当做值的一部分,尤其是空格。

  • 创建资源文件:
  1. 在工程src中新建后缀为.properties的文件

  2. 编写内容:

    内容以key=value的方式呈现,= 左右两边不要有空白,key值抵拢,value后没有符号,key和value值不能使用引号,通过 修饰代表注释。

  • 解析资源文件:

    针对properties文件的读取是使用JDK中的Properties类进行数据读取。

    读取步骤

    1. 创建Properties对象

      Properties prop = new Properties();

    2. 加载资源文件流(有三种方式)

      方式一:传统IO流

      prop.load(new FileInputStream(path)) 输入流加载资源文件

      方式二:字节码对象获取流

      类名.class. getResourceAsStream(/包路径/文件名) 注:字节码对象获取流参数的规则是前必须带/,若资源文件没有在包中,包的路径可以省略,其底层本质还是调用类加载器实现。

      方式三:类加载器获取流(效率最高)

      类加载器是JVM内部专门设计用于加载字节码文件到内存的一个工具

      步骤:

      1. 得到类加载器

        字节码对象.getClassLoader() 字节码对象方式获取类加载器

        Thread.currentThread().getContextClassLoader() 线程方式获取类加载器

      2. 获取流

        类加载器对象.getResourceAsStream(包名/文件名)

        注:类加载器获取流的时候,路径最前方不需要书写/

    3. 读取数据

      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);
    	}
    }