0


【实际开发01】- 单元测试 ( 追求正确性 )


0. 单元测试 概念 / 解析

单元测试 , 就是针对最小的功能单元编写测试代码

在 Java 中 , 最小的功能单元就是方法 , 因此 , 对 Java 程序员进行单元测试实际上就是对 Java 方法的测试。

单元测试 : 任何一个小模块、小功能;模块式测试

 1、可以减少出错、缩小调试影响面
 ​
 2、一步步测 , 才是最快的方法;
 ​
 3、完成一部分测试一部分 , 防止自己懵逼!

写完一节 , 就进行单元测试 ;避免追求一气呵成的错误集中情况! (单元测试包 , 需保证唯一)


1. 为什么要进行单元测试

因为单元测试可以确保你编写的代码是符合软件需求和遵循开发规范的。

单元测试是所有测试中最底层的一类测试 , 是第一个环节 , 也是最重要的一个环节 , 是唯一一次能够达到代码覆盖率 100% 的测试 , 是整个软件测试过程的基础和前提。

可以这么说 , 单元测试的性价比是最好的。

微软公司之前有这样一个统计:

bug 在单元测试阶段被发现的平均耗时是 3.25 小时 , 如果遗漏到系统测试则需要 11.5 个小时。

经我这么一说 , 你应该已经很清楚单元测试的重要性了。

那在你最初编写测试代码的时候 , 是不是经常这么做?就像下面这样。


1. JUnit ~ @Test

IDEA 会自动在当前类所在的包下生成一个类名带 Test ( 惯例 ) 的测试类


2. IDEA 中使用 junit 单元测试 , 不能使用 Scanner 的解决方法

参考:

解决IDEA中使用junit单元测试不能使用Scanner的方法_花開彼岸天丶的博客-CSDN博客_junit不能接收sacanner 解决IDEA中使用junit单元测试不能使用Scanner的方法

编程时在 IDEA 中引入单元测试 , 并在里面用了 Scanner 时 , 运行时会出一直在跑的问题 :

方案1 : 将测试类的代码放入 main 函数运行

方案2 : 在 IDEA 中点击 help -> Edit Custom Vm Options… , 进入 , 在最后一行加入:-Deditable.java.test.console=true

最后 : 在 eclipse 中则不会出现这个问题;


3. Junit 测试 Tutorial


1. daiding


4. @Test 修饰的方法必须 public


1. validatePublicVoidNoArgMethods(Test.class, false, errors);

查阅 Junit 源码 :

     protected void validateInstanceMethods(List<Throwable> errors) {
         
         validatePublicVoidNoArgMethods(After.class, false, errors);
         validatePublicVoidNoArgMethods(Before.class, false, errors);
         
         validateTestMethods(errors);
         if (computeTestMethods().size() == 0)
             errors.add(new Exception(" No runnable methods "));
     }
 ​
     protected void validateTestMethods(List<Throwable> errors) {
         validatePublicVoidNoArgMethods(Test.class, false, errors);
     }

这表明 @Before、@After、@Test 注解的方法必须是 : public,void,非静态,不带参数。


2. public static void main(String[] args) {} ~ 程序入口

Main 方法是 Java 程序的入口

记住,我们这里不会讨论 Servlet、MIDlet 和其他任何容器管理的 java 程序,在 java 核心编程中,JVM 会查找类中的

public static void main(String[]args)

,如果找不到该方法就抛出错误

NoSuchMethodError:main

程序终止。

Main 方法必须严格遵循它的语法规则,方法签名必须是 public static void,参数是字符串数组类型,如果是 Java1.5 及以后的版本还可以使用可变参数:

publicstaticvoid****main(String... args)

今天终于搞懂了:为什么 Java 的 main 方法必须是 public static void?

Public:访问权限最大。

static:不需要对象,直接类名即可。

void:主函数没有返回值。

Main:主函数特定的名称。

(String[] args):主函数的参数,是一个字符串数组类型的参数,jvm 调用 main() 方法时,传递的实际参数是 new String[0]。

 jvm 默认传递的是长度为0的字符串数组,
 我们在运行该类时,也可以指定具体的参数进行传递。
 可以在控制台,运行该类时,在后面加入参数。参数之间通过空格隔开。jvm会自动将这些字符串参数作为args数组中的元素,进行存储。

1. main 概念 / 解析

如果需要用 java 命令直接运行一个 Java 类 , 这个 Java 类必须包含 main 方法 ,

这个 main 方法必须使用 public 和 static 来修饰 , 必须使用 void 声明该方法的返回值 ,

而且该方法的参数类型只能是一个字符串数组 , 而不能是其他形式的参数。

对于这个 main 方法而言 , 前面的 public 和 static 修饰符的位置可以互换 , 但其他部分则是固定的。

定义 main 方法时 , 不要写成 Main 方法 , 如果不小心把方法名的首字母写成了大写 , 编译时不会出现任何问题 , 但运行该程序时将给出如图 2 的错误提示:

这个错误提示找不到 main 方法 , 因为 Java 虚拟机只会选择从 main 方法开始执行。

对于 Main 方法 , Java 虚拟机会把该方法当成一个普通方法 , 而不是程序的入口。

main 方法里可以放置程序员需要执行的可执行性语句 , 例如 System.out.println("Hello Java!") ,

这行语句是 Java 里的输出语句 , 用于向控制台输岀“Hello Java!”这个字符串内容 , 输出结束后还输出一个换行符。

在 Java 程序里执行输岀有两种简单的方式:

System.out.print(需要输出的内容) 和 System.out.println (需要输出的内容) , 其中前者在输出结束后不会换行 , 而后者在输出结束后会换行。

总结:

1、main 方法必须声明为 public、static、void,否则 JVM 没法运行程序 。

2、如果 JVM 找不到 main 方法就抛出 NoSuchMethodError:main 异常,

 例如:如果你运行命令:java HelloWrold,JVM 就会在 HelloWorld.class 文件中搜索 public static void main (String[] args) 方法。

3、main 方式是程序的入口,程序执行的开始处。

4、main 方法被一个特定的线程 ”main” 运行,程序会一直运行直到 main 线程结束或者 non-daemon 线程终止。

5、当你看到

“Exception in Thread main”

如:

Excpetion in Thread main:Java.lang.NullPointedException

,意味着异常来自于 main 线程。

6、你可以声明 main 方法使用 java1.5 的可变参数的方式 ; 如:publicstaticvoid main(String... args)。

7、除了 static、void、和 public,你可以使用 final,synchronized、和 strictfp 修饰符在 main 方法的签名中,如:

public strict fp final synchronized static void main(String[] args)

8、main 方法在 Java 可以像其他方法一样被重载,但是 JVM 只会调用上面这种签名规范的 main 方法。

9、你可以使用 throws 子句在方法签名中,可以抛出任何 checked 和 unchecked 异常。

10、静态初始化块在 JVM 调用 main 方法前被执行,它们在类被 JVM 加载到内存的时候就被执行了。


1. 为什么 main 方法是静态的(static)

1、正因为 main 方法是静态的,JVM 调用这个方法就不需要创建任何包含这个 main 方法的实例。

2、因为 C 和 C++ 同样有类似的 main 方法作为程序执行的入口。

3、如果 main 方法不声明为静态的,JVM 就必须创建 main 类的实例,因为构造器可以被重载,JVM 就没法确定调用哪个 main 方法。

4、静态方法和静态数据加载到内存就可以直接调用 , 而不需要像实例方法一样创建实例后才能调用,如果 main 方法是静态的,那么它就会被加载到 JVM 上下文中成为可执行的方法。


2. 为什么main 方法是公有的 ( public )

Java 指定了一些可访问的修饰符 ;如:private、protected、public,任何方法或变量都可以声明为 public,Java 可以从该类之外的地方访问。

因为 main 方法是公共的,JVM 就可以轻松的访问执行它。


3. 为什么 main 方法没有返回值 ( void )

因为 main 返回任何值对程序都没任何意义,所以设计成 void,意味着 main 不会有任何值返回。


2. 使用

main()

方法来测试有很多坏处

1、测试代码没有和源代码分开。

2、不够灵活 , 很难编写一组通用的测试代码。

3、无法自动打印出预期和实际的结果 , 没办法比对。

JUnit 的话 , 就不会再有这种困扰了。

我可以非常简单地组织测试代码 , 并随时运行它们 , 还能给出准确的测试报告 , 让你在最短的时间内发现自己编写的代码到底哪里出了问题。


3.

String[]

字符串数组


3. Junit 单元测试 和 main 函数区别 ( 踩坑 )


1. Junit 单元测试 不支持 多线程

当 Thread 了新的线程后 , Junit 单元测试 在主线程运行结束后就关闭了 , 而不会等子线程运行结束。

而 main 函数就不存在这个问题了...

测试对比如下:


1. Junit

书写一个定时任务 , 每500ms执行一次:

运行结果:

可以看出 , 当主线程执行结束后 , 就关闭了 , 不会等 子线程运行


2. main 函数

同上 , 书写一个定时任务 , 每 500ms 执行一次:

运行结果如下:


3. 解决方案

如果想使用

Junit

进行多线程测试 , 可以先睡眠主线程 , 例如:

    @Test
    public void test(){
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+ ": ==========Junit 定时任务 =========");
            }
        } ,  new Date() ,  500);

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+ "==========Junit 主线程任务=========");
    }

2. 结论 : 尽量不要在 @test 开启新线程

@Test 单测方法和 main 方法

 在做一个多线程测试的时候发现的一个比较好玩的问题 , 
     在@Test的单测方法中开启了两个线程 , 本来应该在B()方法中的打印 , 没打出来 , 
     看了一下单测方法的线程是Thread[main , 5 , main] , 这方法开启的是main线程。
 ​
 网上一些人的观点是单测方法结束的时候会把里面的资源释放了 , 导致里面线程提前结束。
public class 多线程测试01 {
    int a ;
    boolean flag ;

    public static void main(String[] agrs) {
        多线程测试01 shen = new 多线程测试01();
        new Thread(new Runnable() {
            @Override
            public void run() {
                shen.A();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                    shen.B();
            }
        }).start();
    }

    // @Test 单测方法有一个问题 , 当单测方法结束的时候里面的线程也要结束 , 不管是不是执行完成
    // 结论不要在@Test开启线程
    @Test
    public void play() {
        多线程测试01 shen = new 多线程测试01();
        new Thread(new Runnable() {
            @Override
            public void run() {
                shen.A();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                shen.B();
            }
        }).start();

        System.out.println(Thread.currentThread());

    }

    public void A() {
//        try {
//            Thread.sleep(100);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        a = 1;
        flag = true;
    }

    public void B()  {
        if (flag) {
            System.out.println("flag = true  " + a);
        } else if(!flag){
            System.out.println("falg = false  " + a);
        }
    }
}
标签: 单元测试 java junit

本文转载自: https://blog.csdn.net/weixin_43755082/article/details/128581538
版权归原作者 猫猫聚会Ing 所有, 如有侵权,请联系我们删除。

“【实际开发01】- 单元测试 ( 追求正确性 )”的评论:

还没有评论