舒服的代码和不舒服的代码,差距是怎样的?范德成
2017年9月19日
转载本人回答:
谢邀。混血王子和匿名用户已经回答得很好了。再补充几点:
Discussion with Lincoln Yu about safeguarding wheels with TDD
Decheng "Robbie" Fan
2017-09-14
Yeah, making one's own wheels is a virtue of a programmer! I do believe in this. That's why I'm now looking into TDD as a part of the solution. You know for personal programs it takes a lot of time to test it. TDD can't replace code review--code quality itself still needs to be ensured manually--but it can replace a lot of the test work. This would help a lot when enhancing the wheels with new features.
Regarding how to use TDD effectively:
1. Do pair programming only for critical code. I like TDD but do not like pair programming with regards to final quality (not just because the programming efficiency is lower, but also because pair programming is a dynamic process, which involves little code review). Critical code can be worked through pair programming because feedback is important for it;
1.1 Do pair revision. Write code separately with your colleague, and finally review each other's code. Write review feedback, and do pair revision (update the code) based on the review feedback;
2. The "evolution" concept of TDD is valuable. It helps you grow the test cases and the implementation code gradually, accumulating a lot of valuable test cases and letting you think about the implementation logic from easy to difficult, and from incomplete to complete. Sometimes this evolution requires you to throw away a whole incomplete implementation, because it's incompatible with the complete solution. But it has already made you think more, which is good. Write unit tests--tests red--update the implementation--tests green--write more unit tests--repeat until implementation done. After all unit tests are built, add more test cases by using traditional techniques (Book: How We Test Software at Microsoft) to ensure better test strength, and perform a complete code review (implementation should be reviewed carefully; unit tests should be gone through. Reference: http://www.fandecheng.com/personal/interests/programming/program-error-prev.htm);
3. I have used some mock frameworks and have discovered that mock frameworks (aka mocks) are more evil than mock classes (aka fakes). Mock classes can be debugged and are flexible, while mock frameworks are rigid, introducing higher coupling between implementation and tests and can't be debugged. One step further, single layer mock classes are OK if we just use them to isolate the implementation details of the depended-on components (DOCs, including libraries and frameworks), but they are not enough if we want to do integration test. So I would do two things:
3.1 For unit tests that do use single layer mock classes, perform complete DOC tests first (manually or automatically) to verify their behavior, and record their behavior down (as comments in the unit tests). For example, write experiment code to test NTFS behavior, and write down how NTFS behaves. Then build the mock classes based on the behavior observed;
3.2 For unit tests that do integration test, it means that we need to implement "deep and real" mock classes. These mock classes are very like real implementations of the DOCs. The only difference is that they use virtualized storage (such as a database dedicated to unit test rather than a real one) and environment (such as a virtual network server dedicated to unit test, but not a real one). Such virtualized storage and environment are automatically initialized before each test case, so that the repeatability of the unit tests can be kept. For example, to do integration test of your application with FAT32, implement an in-memory version of FAT32 and perform the test.
编程世界中的“脚手架”——未完成代码白盒单元测试
范德成
2017年10月26日
什么叫“未完成代码白盒单元测试”呢?是这样的。当你写一个可测试的方法时,首先,它可测试,不硬编码地依赖外部环境。其次,它没有写完,从头开始往后写的过程中,可能由于逻辑比较复杂,或者包含复杂的算法,因此一次正确写完会有些困难。此时,“未完成代码白盒单元测试”就可以有所帮助。
以传统的、无单元测试的代码编写方式来看,这种复杂逻辑本身的质量保证可以通过两种方式来完成。一是代码复查。二是用调试器观察或者打日志。而有了“未完成代码白盒单元测试”后,代码复查将变得可选(建议写完整个方法后再复查),而调试器和打日志则完全没有必要了。
那么怎么来做呢?你可能已经猜到了,就是对写好的那部分代码产生的中间结果,用单元测试加以验证。等整个方法都写完后,这部分针对中间结果的单元测试都已经绿了,针对整个方法的单元测试就可以开始建立了。注意,由于要提取中间结果,很可能需要把一些数据保存在某些地方,比如一些公有变量里面,以允许单元测试来读取,所以实际上是破坏了程序本该有的封装的,并且那些保存语句对于功能来说也是多余的。
由于上述原因,当针对整个方法的单元测试开始建立以后,保存中间结果的语句和变量,以及“未完成代码白盒单元测试”就都可以被删除了。这和建筑领域的“脚手架”作用类似——在建房子的过程中用到,建完房子就拆除。总之,就我的经验来看,“脚手架”技术对于包含复杂逻辑(主要是复杂算法)的方法很有用。希望它对你也有帮助。
|