animation 先执行一次 在持续执行_由一次单测执行成功引起的思考

edd148a3a84b215eec01355f168af18a.png

About Junit

在整个java生态系统里面,junit无疑是使用最广泛的单元测试框架之一。目前稳定的版本已经更新到了junit4.12,同时2019 junit4.13的beta版本也进行了发布。除此之外,junit5也更新到了5.4.0。从整个maven的引用量上看。junit4的引用量为86162,junit5为3032。单从目前来看。在进行单元测试框架的选择上,junit4肯定是首选。随着junit5对jdk8新特性的支持,相信它的应用也会越来越广泛。

About Assert

在org.junit.Assert类里面,定义了很多静态方法,用来判断期望结果与实际结果是否一致。两个对象或基本数据类型是否一致是一个比较口语化的说法。那么我们具体看看junit框架是如何来进行判断的。

Assert Object

如果期望结果和实际结果是java的对象。Junit在进行assert时,调用的重载方法为assertEquals(Object expected, Object actual),一步步跟进到该方法的底层实现。最终会发现在进行两个对象相等判断时,用到的是Object类的equals方法。equals的底层实现是 ==判断。

b1580abf405f2aa827c537a8ce3dfd58.png

b04fb9181c0b117efb16b531c0802d77.png

通过以上的判断,两个Object对象进行assert判断的话,如果两个对象的引用指向的是同一个对象实例,那么assertEquals为true,否则为false。

为了验证,笔者定义了两个Object对象实例,同assertEquals来进行判断是否一样。验证的结果是assert失败了。因为object1与object2指向的是不同的对象实例。

59798deb4545036e43a97703a881606f.png

但是笔者在实际使用中却发现,在单测用例中定义了两个不同的对象实例,最终assert结果却是true。

f6814309d3812cb425dde8a2e3029480.png

验证到这里时,笔者感觉有点儿疑惑了。难道刚才的说法有错误吗?为了再次验证刚才的说法。笔者对刚才的单测用例做了一个小的修改。在两个对象创建的代码间添加了一秒的等待,然后再次执行该测试用例。最终结果是验证失败了。

f9d88399c725ea9d0299961ed7be5f57.png

重写-Override

在java中,所有的类都隐式的继承自Object类。在Object类中定义了11个方法。跟本文相关的为equals方法。

df42ec374b66d728fc162775014e0bc0.png

Java Util Date类也继承自Object,同时Date类override了父类的equals方法。

edc353b46a4b5e9e02c8de078e8cc407.png

根据java多态的特性,当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。因此在比较date1与date2时,实际上调用的是Date类的equals方法。在equals方法里面,实际上比较的是两个Date实例的从1970年到实例创建完成的毫秒数。

8dcd5b9c4fff6e63c7f09774a29ff741.png

通过验证的结果来看,可以反推出来date1与date2的getTime是一样的。改造一下测试用例,来比较两个实例的毫秒数。结果是true。

c0b2ec422c57832483bc079a055a5293.png

深入分析

写到这里,笔者好像知道原因为什么date1与date2是一样的了。那么是不是可以反推出来,Date date1 = new Date(); Date date2 = new Date2(); 在这两个实例创建的时候用时是一样的呢?

然后为了验证这个推断,将之前的测试用例再做一次改造。计算一下实例化两个Date实例的用时。发现多次执行的时间差结果每次还都不太一样,但是assert的结果都是true。

bad303d849b298bd30103c171e4a24a6.png

b52aaff368de1d2b0236f428b7fb0222.png

8204fb4aac2849765e03c1247932c989.png

经过上面的验证,笔者又一次不知道该如何解释底层的原因了。莫非在Date实例化的时候JVM做了什么特殊的处理?因为测试用例中用到的类初始化方法为new Date();那么我们看看new()都具体做了什么?代码跟进的结果可以看到,new Date()实质上就是用System.currentTimeMillis()初始化了一个Date对象。然后将该参数赋给了Date类的fastTime属性。

051b31025e755005a3705aa1a4e141f3.png

84c06d36f1c3bcfd608b980409513993.png

所以在进行equals判断时,直接用的fastTime属性进行比较的。

50cd5a06680b78e873756123af801846.png

再次迷茫

写到这里笔者已经越来越迷糊了。到底是哪儿出了问题。再次改造测试用例。将date1 date2的getTime()获取到的值打印出来进行比较。可以从本次的验证推断出来问题不是出来middle-start和end-middle上,而在初始化类的时候对fastTime赋值上。

605e25cc4b5b86704eab68edf2fff0d1.png

针对上面的假设,再次对测试用例做改造,通过反射的方式获取到私有变量fastTime的值。这次无论执行多少次,获取到的两个实例的fastTime都是相同的。

de2436b3ebea5e4d9fc05bc47415fec5.png

到目前为止,就可以把问题的范围控制在fastTime上了。就是说在进行初始化的时候。虽然调用了两次System.currentTimeMillis()方法但是最终的结果却是一样的问题上。

毫秒vs纳秒

因为System.currentTimeMillis()取得值单位是毫秒,如果这两个时间之间处理小于1毫秒的话,当然执行时间为0。但是用更精确的纳秒时,发现还是有区别的。差别的数值跟测试的机器有关系,性能越高差距越小。

e28db5a8591eb3349dec4fead6e3f66b.png

更深入的分析

限于笔者对java有限的认识,以上分析过程个人感觉还是比较表面化。目前来看就只能分析到此。读者如果有不同的理解或者分析方法,可以一同讨论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值