一个演示Junit4实现原理的ut

文|码术张

本文旨在说明Junit4源代码的设计思想。
说明方式上,使用的是自己的一个创新的想法:
用一个ut来说明。

代码如下;

package test;

import org.junit.Test;

public class IpTest {

    @Test
    public void should_True() throws Exception{
        MyNotifier notifier = new MyNotifier();
        MyResult result = new MyResult();
        notifier.addListener(result.createListener());
        notifier.addListener(new Detail());
        MyDescription description = MyDescription.createMyDescriptionn("IpTest", "");

        notifier.fireTestRunStarted(description);

            MyDescription descriptionForA = MyDescription.createMyDescriptionn("IpTest", "methodA");

            notifier.fireTestStarted(descriptionForA);
            System.out.println("Hi..., I am method A");
            notifier.fireTestFinished(descriptionForA);

            MyDescription descriptionForB = MyDescription.createMyDescriptionn("IpTest", "methodB");
            notifier.fireTestStarted(descriptionForB);
            System.out.println("Hi..., I am method B");
            notifier.fireTestFinished(descriptionForB);

        notifier.fireTestRunFinished(result);

    }

}

类图:


类图

交互图:


交互图

在class IpTest中,有一个should_True方法。
这个方法的实现,即演示了Junit4的实现原理。
MyNotifier、MyResult、MyDescription,
对就Junit源码中类Notifier、Result、Description。

程序运行时,首先发布一个事件fireTestRunStarted;
运行结束时,发布一个一个事件fireTestRunFinished。

类的每一个方法运行时,首先发布一个事件fireTestStarted;
类的每一个方法结束时,会发现一个事件fireTestFinished。

Listener收到事件后,会做一些操作。
这些操作的结果会在程序结时,打印出来:


运行结果

在Junit源代码中,每个方法会有一个用职责链模式构建一个Statement类的对象。
一个方法的执行,即是依次执行这个链表上的语句。
这点没在本文中体现。

本文所用的其他代码如下:

package test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MyNotifier {
    private final List<MyRunListener> fListeners =
    Collections.synchronizedList(new ArrayList<MyRunListener>());

    public void addListener(MyRunListener listener) {
        fListeners.add(listener);
    }

    public void fireTestRunStarted(final MyDescription description) throws Exception {
        for (MyRunListener each : fListeners) {
                each.testRunStarted(description);
        }

    }

    public void fireTestRunFinished(final MyResult result) throws Exception{
        for (MyRunListener each : fListeners) {
                each.testRunFinished(result);
        }

    }

    public void fireTestStarted(final MyDescription description) throws Exception{
        for (MyRunListener each : fListeners) {
                each.testStarted(description);
        }       

    }

    public void fireTestFinished(final MyDescription description)   throws Exception{
        for (MyRunListener each : fListeners) {
                each.testFinished(description);
        }   

    }
}

package test;

import java.util.concurrent.atomic.AtomicInteger;

public class MyResult {

    private AtomicInteger fCount = new AtomicInteger();
    private long fRunTime = 0;
    private long fStartTime;

    public int getRunCount() {
        return fCount.get();
    }

    public long getRunTime() {
        return fRunTime;
    }   

    private class Listener extends MyRunListener {
        @Override
        public void testRunStarted(MyDescription description) throws Exception {
            fStartTime = System.currentTimeMillis();
        }

        @Override
        public void testRunFinished(MyResult result) throws Exception {
            long endTime = System.currentTimeMillis();
            fRunTime += endTime - fStartTime;
        }

        @Override
        public void testFinished(MyDescription description) throws Exception {
            fCount.getAndIncrement();
        }

    }   

    public MyRunListener createListener() {
        return new Listener();
    }
}

package test;

import org.junit.runner.notification.Failure;

public class Detail  extends MyRunListener {
    public void testRunStarted(MyDescription description) throws Exception {
        println("==>JUnit4 started with description: \n" + description);
        println();

      }

      public void testRunFinished(MyResult result) throws Exception {
        println("==>JUnit4 finished with result: \n" + describe(result));
      }

      public void testStarted(MyDescription description) throws Exception {
        println("==>Test method started with description: " + description);
      }

      public void testFinished(MyDescription description) throws Exception {
        println("==>Test method finished with description: " + description);
        println();
      }

      public void testFailure(Failure failure) throws Exception {
        println("==>Test method failed with failure: " + failure);
      }

      public void testAssumptionFailure(Failure failure) {
        println("==>Test method assumption failed with failure: " + failure);
      }

      public void testIgnored(MyDescription description) throws Exception {
        println("==>Test method ignored with description: " + description);
        println();
      }

      private String describe(MyResult result) {
        StringBuilder builder = new StringBuilder();
        builder.append("\tRunCount: " + result.getRunCount())
            .append("\n");
        ;
        builder.append("\tRunTime: " + result.getRunTime())
            .append("\n");

        ;
        return builder.toString();
      }

      private void println() {
        System.out.println();
      }

      private void println(String content) {
        System.out.println(content);
      }
}

package test;

public class MyDescription {
    private  String fclassName;
    private  String fMethodName;

    public String getMethodName() {
        return fMethodName;
    }

    public String getClassName() {
        return fclassName;
    }

    @Override
    public String toString() {
        return getDisplayName();
    }

    /**
     * @return a user-understandable label
     */
    public String getDisplayName() {
        return formatDisplayName(fMethodName, fclassName);
    }

    private MyDescription(String className, String methodName) {
        fclassName = className;
        fMethodName = methodName;
    }

    public static MyDescription createMyDescriptionn(String className, String methodName) {
        return new MyDescription( className,  methodName);
    }

    private static String formatDisplayName(String name, String className) {
        return String.format("%s(%s)", name, className);
    }

}

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容