![edd148a3a84b215eec01355f168af18a.png](https://i-blog.csdnimg.cn/blog_migrate/fe53e64f1bea946727c664ee223f7795.jpeg)
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](https://i-blog.csdnimg.cn/blog_migrate/c82a2e02b7e52675cbf03695429992af.png)
![b04fb9181c0b117efb16b531c0802d77.png](https://i-blog.csdnimg.cn/blog_migrate/1958eddc58f02657da2ca194711769c3.png)
通过以上的判断,两个Object对象进行assert判断的话,如果两个对象的引用指向的是同一个对象实例,那么assertEquals为true,否则为false。
为了验证,笔者定义了两个Object对象实例,同assertEquals来进行判断是否一样。验证的结果是assert失败了。因为object1与object2指向的是不同的对象实例。
![59798deb4545036e43a97703a881606f.png](https://i-blog.csdnimg.cn/blog_migrate/ced11fc14f7e7b3965ee98e27b7e63fc.jpeg)
但是笔者在实际使用中却发现,在单测用例中定义了两个不同的对象实例,最终assert结果却是true。
![f6814309d3812cb425dde8a2e3029480.png](https://i-blog.csdnimg.cn/blog_migrate/cf61eb3b8ad55cda0251b732cebc1fcf.jpeg)
验证到这里时,笔者感觉有点儿疑惑了。难道刚才的说法有错误吗?为了再次验证刚才的说法。笔者对刚才的单测用例做了一个小的修改。在两个对象创建的代码间添加了一秒的等待,然后再次执行该测试用例。最终结果是验证失败了。
![f9d88399c725ea9d0299961ed7be5f57.png](https://i-blog.csdnimg.cn/blog_migrate/eb007bffae51f2f5dd16265168e61fac.jpeg)
重写-Override
在java中,所有的类都隐式的继承自Object类。在Object类中定义了11个方法。跟本文相关的为equals方法。
![df42ec374b66d728fc162775014e0bc0.png](https://i-blog.csdnimg.cn/blog_migrate/28cc88b112b8c39f1cd8ac263d3c8126.png)
Java Util Date类也继承自Object,同时Date类override了父类的equals方法。
![edc353b46a4b5e9e02c8de078e8cc407.png](https://i-blog.csdnimg.cn/blog_migrate/d10005e7d647a0db37e8643f96191fbc.png)
根据java多态的特性,当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。因此在比较date1与date2时,实际上调用的是Date类的equals方法。在equals方法里面,实际上比较的是两个Date实例的从1970年到实例创建完成的毫秒数。
![8dcd5b9c4fff6e63c7f09774a29ff741.png](https://i-blog.csdnimg.cn/blog_migrate/d769ddd0dc130732f1218ab4e9b08090.jpeg)
通过验证的结果来看,可以反推出来date1与date2的getTime是一样的。改造一下测试用例,来比较两个实例的毫秒数。结果是true。
![c0b2ec422c57832483bc079a055a5293.png](https://i-blog.csdnimg.cn/blog_migrate/f1bfe510ad36b2838c3ff0a0f9527c40.jpeg)
深入分析
写到这里,笔者好像知道原因为什么date1与date2是一样的了。那么是不是可以反推出来,Date date1 = new Date(); Date date2 = new Date2(); 在这两个实例创建的时候用时是一样的呢?
然后为了验证这个推断,将之前的测试用例再做一次改造。计算一下实例化两个Date实例的用时。发现多次执行的时间差结果每次还都不太一样,但是assert的结果都是true。
![bad303d849b298bd30103c171e4a24a6.png](https://i-blog.csdnimg.cn/blog_migrate/9624a9dde6d6b7f1f4521e856f119c31.jpeg)
![b52aaff368de1d2b0236f428b7fb0222.png](https://i-blog.csdnimg.cn/blog_migrate/08b829b942394c678c44ff899ac876b1.jpeg)
![8204fb4aac2849765e03c1247932c989.png](https://i-blog.csdnimg.cn/blog_migrate/d0b8e063b8af67fca5b2afd528cd694a.jpeg)
经过上面的验证,笔者又一次不知道该如何解释底层的原因了。莫非在Date实例化的时候JVM做了什么特殊的处理?因为测试用例中用到的类初始化方法为new Date();那么我们看看new()都具体做了什么?代码跟进的结果可以看到,new Date()实质上就是用System.currentTimeMillis()初始化了一个Date对象。然后将该参数赋给了Date类的fastTime属性。
![051b31025e755005a3705aa1a4e141f3.png](https://i-blog.csdnimg.cn/blog_migrate/da93d73ffbf2d003e4013b187af0893e.jpeg)
![84c06d36f1c3bcfd608b980409513993.png](https://i-blog.csdnimg.cn/blog_migrate/711f409c713d1f2e9b67adeca9b8a599.jpeg)
所以在进行equals判断时,直接用的fastTime属性进行比较的。
![50cd5a06680b78e873756123af801846.png](https://i-blog.csdnimg.cn/blog_migrate/3f0974ccfe006914069757ce66fe9fdd.jpeg)
再次迷茫
写到这里笔者已经越来越迷糊了。到底是哪儿出了问题。再次改造测试用例。将date1 date2的getTime()获取到的值打印出来进行比较。可以从本次的验证推断出来问题不是出来middle-start和end-middle上,而在初始化类的时候对fastTime赋值上。
![605e25cc4b5b86704eab68edf2fff0d1.png](https://i-blog.csdnimg.cn/blog_migrate/92a978de7675255980a78d0305521570.jpeg)
针对上面的假设,再次对测试用例做改造,通过反射的方式获取到私有变量fastTime的值。这次无论执行多少次,获取到的两个实例的fastTime都是相同的。
![de2436b3ebea5e4d9fc05bc47415fec5.png](https://i-blog.csdnimg.cn/blog_migrate/3a94c55478d2842e71a3ffc441cc4bec.jpeg)
到目前为止,就可以把问题的范围控制在fastTime上了。就是说在进行初始化的时候。虽然调用了两次System.currentTimeMillis()方法但是最终的结果却是一样的问题上。
毫秒vs纳秒
因为System.currentTimeMillis()取得值单位是毫秒,如果这两个时间之间处理小于1毫秒的话,当然执行时间为0。但是用更精确的纳秒时,发现还是有区别的。差别的数值跟测试的机器有关系,性能越高差距越小。
![e28db5a8591eb3349dec4fead6e3f66b.png](https://i-blog.csdnimg.cn/blog_migrate/348494f06e7a3cc9fe6efcb8d0d125e7.jpeg)
更深入的分析
限于笔者对java有限的认识,以上分析过程个人感觉还是比较表面化。目前来看就只能分析到此。读者如果有不同的理解或者分析方法,可以一同讨论。