Java API:1. unit test and main function


0. 抛出企业问题,脱离main测试,模块化概念抛出

在Java编程中,main方法是程序的入口点。通常来说,main方法的职责是启动程序,而不是处理复杂的业务逻辑。因此,我们应该遵循模块化编程的原则,将具体的功能逻辑从main方法中抽离出来,从而使得代码更清晰、易维护,并便于测试。

0.1 main方法的作用

main方法是程序的启动入口,它的主要作用是启动应用程序、初始化一些基本操作、以及调用其他模块中的功能。我们可以把main方法看作是"门",它的作用仅仅是打开和关闭程序的执行流,真正的业务逻辑应该放在其他地方。

0.2 模块化编程

模块化编程的思想是将功能模块独立出来,使每个模块都有明确的职责。这样一来,程序的可维护性和可扩展性大大增强。main方法仅作为程序的入口,不应该包含业务逻辑。在具体的应用开发中,复杂的计算和操作应当被封装在其他函数或类中。

例如,我们可以编写一个简单的求和函数,将其从main方法中提取出来,保持main方法的简洁。

0.3 示例代码:模块化编程
public class Main {
    /*
    main方法就是进入程序的一扇门,而这个门只负责开和关,打开门可以实现某些功能,但这些功能只由门里面的东西来决定,而不是门
    main是一个程序的入口点,而不应该是处理逻辑或者功能模块的点,main方法中的语句不应该有逻辑性的语句,而且代码应该是非常之少的,更复杂的东西不应该出现在main里,应该抽离main
    */

    // 以往方式:main()  在main里测试
    public static void main(String[] args) {
        // 3. 调用函数
        int number = sum(1, 2);

        // 4. 看输出,程序员通过肉眼看控制台打印输出,查看结果是否符合预期
        System.out.println(number);

        // 5. 预期结果和测试结果是通过人工计算的。
    }

    // 1. 编写功能函数(方法)
    public static int sum(int number_a, int number_b) {
        return number_a + number_b;
    }
}
0.4 分析
  • 功能函数(sum方法):我们将功能函数sum提取出来,该函数接受两个整数作为参数,返回它们的和。这样,sum方法单独负责具体的计算逻辑。
  • main方法main方法现在只是负责调用sum函数,打印输出结果。它不再承担具体的逻辑,而是作为程序的启动点。
0.5 总结

在开发中,我们应该始终遵循“单一职责原则”,即每个模块或函数都应该只负责一个功能。通过模块化和函数抽离,不仅可以让代码更清晰,还能提高程序的可维护性和可测试性。main方法应尽量简洁,复杂的功能逻辑应该抽离到其他函数或类中,这样更符合企业级应用的设计标准。


1. Junit单元测试的含义和用途

在软件开发过程中,尤其是团队合作的项目中,代码的功能和质量检查至关重要。通常,项目只会有一个main函数,但一个完整的项目是由多人合作完成的。为了避免将所有人的工作合并后再进行测试,这样不仅耗时且容易出现问题,如何高效地进行代码测试就成为了一个重要问题。Junit单元测试提供了一个有效的解决方案。

1.1 什么是单元测试?

单元测试(Unit Testing)是指对程序中最小的可测试单元进行验证的过程。这里的“单元”指的是程序中的单个类或方法。每个单元的测试独立进行,确保每个单元按预期工作,且可以单独运行。单元测试的目的是验证某个函数或方法的功能是否正确、符合预期。

就像在小学的英语学习中,我们会将一本书分成几个单元,每个单元测试一次,确保每个单元的内容都能被学生掌握并正确记忆。在软件开发中,单元测试也将整个程序划分为多个“小块”进行逐个测试,确保每个模块或方法都能按预期运行。

1.2 为什么使用单元测试?
  1. 减少集成问题:团队开发时,成员之间的代码往往会互相影响。通过单元测试,能够在每个开发阶段及时发现并修复代码中的问题,避免在最终集成时发现错误。
  2. 提高代码质量:单元测试可以帮助开发人员发现代码中的潜在问题或不符合预期的行为,从而提高代码的稳定性和质量。
  3. 简化维护:在代码修改或扩展时,单元测试可以帮助确保修改后的代码没有破坏已有功能,减少后期维护的复杂度。
  4. 自动化测试:通过使用JUnit等框架,开发人员可以自动化执行测试,节省手动测试的时间,提高工作效率。
1.3 JUnit单元测试框架

JUnit是最常用的Java单元测试框架,它提供了许多方法和注解,用于简化单元测试的编写和执行。JUnit通过将测试代码与业务代码分离,使得测试可以独立于主程序运行,从而达到模块化、快速验证代码功能的效果。

1.4 小结

通过单元测试,我们将一个复杂的程序分解为小单元进行逐个测试,确保每个方法和类能够正确实现其功能。在多人合作的开发环境中,单元测试不仅帮助开发人员验证功能,还能提高团队的协作效率,减少错误和问题。JUnit作为最流行的测试框架,提供了强大的支持,帮助开发者高效地进行单元测试,从而提高软件质量和开发效率。


2. Maven Repository 获取JUnit.jar

在Java项目开发中,JAR包(Java ARchive)是一个重要的组件,通常包含了库文件、类文件以及资源文件。许多Java开发框架、库和工具都以JAR包的形式发布,开发者可以将这些JAR包集成到自己的项目中。为了简化管理和获取这些JAR包,Maven Repository提供了一个统一的平台,用于存储和共享各种版本的JAR包。

2.1 什么是Maven Repository?

Maven Repository是一个存储和共享Java库的中央仓库。Maven是一个项目管理和构建自动化工具,它使用中央仓库(Maven Central Repository)来下载项目所依赖的JAR包。在Maven Repository中,开发者可以方便地查找、下载并管理各种Java依赖库。

Maven Repository中包含了成千上万的Java库,几乎涵盖了所有主流的Java框架和工具。通过Maven,开发者只需要声明依赖,Maven会自动从中央仓库下载相应的JAR包,并将其集成到项目中。

2.2 如何获取JUnit.jar?

JUnit是Java中最常用的单元测试框架,而JUnit的JAR包通常通过Maven进行获取和管理。下面是如何通过Maven Repository获取JUnit的JAR包:

  1. 访问Maven Repository: 打开Maven中央仓库的官网:https://search.maven.org/

  2. 搜索JUnit: 在搜索框中输入“JUnit”,你将看到不同版本的JUnit框架。你可以选择你需要的版本,例如JUnit 5或者JUnit 4。

  3. 选择版本: 选择所需版本的JUnit框架。以JUnit 5为例,你可以看到类似以下的信息:

    Group ID: org.junit.jupiter
    Artifact ID: junit-jupiter-api
    Version: 5.7.1
    
  4. 添加到Maven依赖中: 获取到JUnit对应的Group IDArtifact IDVersion后,将其添加到你的项目的pom.xml文件中:

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.7.1</version>
        <scope>test</scope>
    </dependency>
    

    这样,Maven会自动从中央仓库下载JUnit相关的JAR包,并将其加入到项目的依赖中。

2.3 为什么使用Maven Repository?
  • 集中管理:通过Maven Repository,所有的JAR包都集中存放在一个地方,开发者可以方便地查找和使用。
  • 自动下载:Maven能够自动解析pom.xml文件中的依赖信息,从Maven中央仓库下载需要的JAR包,避免手动下载和管理。
  • 版本控制:Maven Repository提供了不同版本的JAR包,开发者可以根据需要选择合适的版本,避免版本冲突和不兼容的问题。
  • 简化构建:Maven不仅管理JAR包的下载,还可以处理项目的构建、测试和发布过程。通过配置pom.xml,开发者可以轻松实现自动化构建和依赖管理。
2.4 小结

Maven Repository是一个非常实用的工具,它简化了Java项目中依赖库的管理和获取过程。通过Maven Repository,开发者可以方便地查找、下载并管理各种版本的JAR包,如JUnit等框架。在现代Java开发中,Maven已成为标准工具,帮助开发者高效地管理项目依赖,提高开发效率。


3. 使用JUnit进行单元测试

在Java开发中,单元测试是确保代码功能正确性的重要手段。JUnit是最常用的Java单元测试框架之一,它为我们提供了简洁、有效的方式来编写和执行测试用例。本文将通过一个简单的示例,展示如何使用JUnit测试一个基本的sum方法。

3.1 示例代码:Calc

首先,我们有一个简单的Calc类,其中包含一个用于求和的方法sum

package com.nix.demo;

// 3. 使用Junit
public class Calc {
    public static void main(String[] args) {
        // main方法当前没有实现,主要用于展示
    }

    // 求和方法
    public static int sum(int number_a, int number_b) {
        return number_a + number_b;
    }
}

该类的主要功能是实现两个整数的求和,sum方法接受两个整数参数并返回它们的和。接下来,我们将编写单元测试来验证该方法是否正确。

3.2 编写JUnit测试

为了对sum方法进行测试,我们需要创建一个JUnit测试类。在JUnit中,我们通过编写测试方法来验证程序的行为,测试方法的名字通常以test开头,并且标记为@Test注解。

以下是一个简单的JUnit测试示例:

package com.nix.demo;

import static org.junit.Assert.assertEquals;  // 引入JUnit的断言方法
import org.junit.Test;  // 引入JUnit的Test注解

public class CalcTest {

    @Test
    public void testSum() {
        // 进行求和测试,期望结果是 5
        int result = Calc.sum(2, 3);
        assertEquals(5, result);  // 断言期望结果和实际结果相等
    }
}
3.3 解析测试代码
  1. @Test注解:JUnit使用@Test注解标记一个方法为测试方法。在运行测试时,JUnit框架会自动执行这些方法。
  2. assertEquals(expected, actual):这是JUnit中常用的断言方法。它用于验证实际结果actual是否与期望结果expected相同。如果相同,测试通过;否则,测试失败。
  3. Calc.sum(2, 3):调用我们之前实现的sum方法,传入23,返回5
3.4 运行JUnit测试

在编写好测试类后,运行测试非常简单。在IDE中(如IntelliJ IDEA或Eclipse),你可以直接右键点击测试类,并选择“Run”选项来运行测试。JUnit会自动执行@Test标记的方法,并在控制台输出测试结果。如果所有断言都通过,测试会显示为绿色;否则,JUnit会报告失败的测试,并显示具体的错误信息。

3.5 总结

使用JUnit进行单元测试是确保代码正确性的一个重要步骤。通过将代码逻辑和测试代码分离,我们可以在不影响主程序的情况下快速发现并修复bug。对于Calc类中的sum方法,我们通过编写JUnit测试验证了它的正确性,并通过断言确保它的输出与预期结果一致。JUnit不仅提高了开发效率,还能有效减少程序中潜在的错误。

通过这种方式,我们可以逐步扩展更多的测试用例,涵盖更多的功能和边界情况,确保整个应用的稳定性和正确性。


4. 使用Assert断言进行测试

在前面的操作中,我们已经展示了如何使用JUnit测试方法,但我们依然依赖System.out.println()进行结果输出,并通过目测来判断是否正确。这种方式虽然有效,但并不高效,也不符合自动化测试的最佳实践。为了让测试更加自动化、准确和可靠,我们需要使用JUnit的断言方法,例如Assert.assertEquals()

4.1 什么是断言?

断言是用于测试预期结果和实际结果是否一致的工具。在JUnit中,Assert类提供了多种断言方法,最常用的是assertEquals(expected, actual)。它用于比较期望值(expected)和实际值(actual),如果它们不相等,测试将失败并抛出异常。

在测试过程中,断言可以帮助我们自动化验证功能是否按预期运行,而无需手动打印结果进行对比。

4.2 使用Assert.assertEquals()断言

让我们通过一个例子来进一步理解Assert.assertEquals()的使用。在下面的代码中,我们依然使用了之前的Calc类中的sum方法:

package com.nix.demo;

import org.junit.Assert;
import org.junit.Test;

public class CalcTest {

    // 测试sum方法的正确性
    @Test
    public void sum() {
        // 调用sum方法计算1 + 2
        int sum = Calc.sum(1, 2);
        
        // 使用Assert断言来验证结果是否等于预期的4
        Assert.assertEquals(4, sum);
    }

    // 这个测试有局限性,只有1 + 2能通过,不能验证其他情况
}
4.3 解析代码
  1. @Test注解:和之前的测试一样,我们使用@Test注解标记测试方法。JUnit会自动识别并执行该方法。
  2. Calc.sum(1, 2):调用我们之前编写的sum方法,计算1 + 2的和。
  3. Assert.assertEquals(expected, actual):断言方法assertEquals()会比较sum方法的返回值与预期值4是否相等。如果相等,测试通过;否则,测试失败。

在这个例子中,sum(1, 2)的实际结果是3,但我们通过Assert.assertEquals(4, sum)断言,期望结果是4,显然这会导致测试失败。

4.4 扩展测试场景

虽然上述测试有效,但它存在局限性:只测试了sum(1, 2)这一特定情况。如果我们只通过这种方式来验证代码的正确性,程序会面临很大的风险,因为我们只能验证一个特定场景,而无法全面覆盖。

为了解决这个问题,我们可以扩展更多的测试用例,验证sum方法在不同情况下的表现:

@Test
public void sumTest() {
    // 测试1 + 2
    Assert.assertEquals(3, Calc.sum(1, 2));
    
    // 测试0 + 0
    Assert.assertEquals(0, Calc.sum(0, 0));
    
    // 测试负数相加
    Assert.assertEquals(-1, Calc.sum(-2, 1));
    
    // 测试大数相加
    Assert.assertEquals(1000000000, Calc.sum(500000000, 500000000));
}

在这个扩展的测试中,我们增加了多个测试用例:

  • 测试1 + 2是否等于3
  • 测试0 + 0是否等于0
  • 测试-2 + 1是否等于-1
  • 测试大数500000000 + 500000000是否等于1000000000

通过这种方式,我们可以确保sum方法的正确性,不仅限于某一个输入,而是适用于多种不同的场景。

4.5 小结

使用Assert.assertEquals()断言是JUnit中进行自动化测试的重要手段之一。它帮助我们自动化地验证方法的实际结果与预期结果是否一致,避免了手动打印输出的繁琐和不准确。通过断言,我们不仅能验证基本情况,还能覆盖多种不同的测试场景,从而确保代码在不同条件下的正确性。

通过扩展测试,我们可以确保我们的代码更加健壮,避免因忽视某些场景而导致的潜在问题。随着项目规模的增大,单元测试将成为开发中不可或缺的一部分。


5. 更进一步:合理编写随机测试,完善代码案例

在单元测试中,手动设计的测试用例虽然能验证代码的正确性,但它们通常是有限的,并且可能没有覆盖到所有可能的场景。为了使测试更加全面、具有说服力,我们可以引入随机测试。随机测试通过随机生成输入数据进行测试,从而帮助我们发现一些难以预见的问题,特别是在边界情况和极端数据上。

5.1 随机测试的优点
  • 覆盖更多场景:随机生成的数据能够覆盖更多的输入场景,尤其是在边界值和不常见的情况下。
  • 提高测试的鲁棒性:通过随机测试,可以暴露出在手动设计的测试用例中不容易发现的潜在问题。
  • 自动化:随机测试不依赖于人为的测试设计,可以在较短的时间内测试更多的情况。
5.2 使用ThreadLocalRandom进行随机测试

在下面的例子中,我们使用ThreadLocalRandom类来生成随机整数,并对sumsub方法进行测试。ThreadLocalRandom是Java 8引入的一个工具类,适用于多线程环境中生成高效、随机的数值。

以下是CalcTest类中的随机测试代码:

package com.nix.demo;

import org.junit.Assert;
import org.junit.Test;

import java.util.concurrent.ThreadLocalRandom;

public class CalcTest {

    // 测试sum方法,使用随机生成的两个数
    @Test
    public void sum() {
        // 生成随机整数
        int numberA = ThreadLocalRandom.current().nextInt();
        int numberB = ThreadLocalRandom.current().nextInt();
        
        // 调用sum方法
        int sum = Calc.sum(numberA, numberB);

        // 断言sum方法的结果与预期相等
        Assert.assertEquals(numberA + numberB, sum);
    }

    // 测试sub方法,使用随机生成的两个数
    @Test
    public void sub() {
        // 生成随机整数
        int numberA = ThreadLocalRandom.current().nextInt();
        int numberB = ThreadLocalRandom.current().nextInt();
        
        // 调用sub方法
        int sub = Calc.sub(numberA, numberB);

        // 断言sub方法的结果与预期相等
        Assert.assertEquals(numberA - numberB, sub);
    }
}
5.3 解析代码
  1. ThreadLocalRandom.current().nextInt():使用ThreadLocalRandom来生成随机整数。这些随机数的生成是线程安全的,并且比传统的Random类在多线程环境下更高效。nextInt()方法会生成一个随机整数,默认范围是Integer.MIN_VALUEInteger.MAX_VALUE之间。
  2. Calc.sum(numberA, numberB):调用我们实现的sum方法,计算两个随机数的和。
  3. Assert.assertEquals(expected, actual):断言sum方法的结果是否与预期结果相等。如果相等,测试通过;如果不相等,测试失败。我们期望sum方法返回的结果是numberA + numberB
  4. Calc.sub(numberA, numberB):调用sub方法,计算两个随机数的差。
5.4 扩展随机测试的思路

虽然我们目前只测试了sumsub方法,但你可以根据需要扩展更多的测试场景。比如:

  • 边界值测试:可以通过生成接近Integer.MIN_VALUEInteger.MAX_VALUE的随机数来验证代码是否能处理极端数据。
  • 负数测试:可以通过生成负数测试sumsub方法,确保它们正确处理负数加法和减法。
  • 异常测试:如果方法中有抛出异常的逻辑,可以通过生成特殊的输入来验证异常是否正确抛出。

通过这些扩展,我们可以确保Calc类中的方法在各种情况下都能正常工作,并且不会出现意外的错误。

5.5 小结

通过引入随机测试,我们不仅验证了sumsub方法在常见场景下的正确性,还进一步提高了测试的覆盖率和鲁棒性。随机测试能够暴露出一些传统测试用例无法发现的问题,特别是在边界值和极端数据的情况下。通过不断扩展和完善测试,我们可以确保代码在不同的输入条件下都能稳定运行。

总的来说,随机测试是单元测试中一个非常有效的补充,它能够提高我们代码的质量和可靠性。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值