缺牙的时候,我们不停地试探;软件开发,也应如此

本文探讨了软件开发过程中不断测试与验证的重要性,从个人经验出发,通过多个角度阐述了测试在确保软件质量、避免错误积累等方面的关键作用。从日常生活中汲取灵感,将测试比作确保物品稳定、健康发展的过程,强调了在开发的不同阶段进行测试的必要性。通过实例分析,展示了如何在代码编写、回归测试、自动化工具应用等方面实施有效的测试策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

缺牙的时候,我们不停地试探;软件开发,也应如此


小时候掉牙了的时候,总是用舌头去舔那个缺口,一直到牙齿完全长出来,似乎
想时刻确定它长到了什么程度。最有意思的是,家长告诉,不要去舔,会长歪
的;越是这样,我们越想确定,它歪了么?


我在芬兰有一次坐长途大巴,那个司机的头顶大半秃了。之所以能让我有这么深
刻的印象,是因为那几个小时里,他一直轮换着用一只手去摸头顶。似乎这样能
长出来,或者确定没长出来?


我们在构造物品,搭积木,或者盖房子的时候,无一不是如此。我们在每一个步
骤测试,确定产品(此处应作人工制品,但是这字太小资了)确实按我们计划开
发,这一个小部分,也符合我们的预期。


中间没有任何测试过程,期待直接完成,是愚蠢的。


治病的时候,医生也要不停地测试--抽血,验尿,然后才敢给你下药。我们的所
有行为,都依赖于对世界的观察。


谈恋爱的人们,小心翼翼地试探--今天气你一次,明天考验你一下。揪玫瑰花瓣
这种行为,大抵只能用于测试智商,或者投入程度了。


软件开发中,我们也需要在每个阶段,测试每个单元是否按我们预想的那样工作。
如果"是",那么我们继续。如果"不是",我们要做的不是继续突进,而是停下来
想想哪里出了毛病。这也是我们设计实验和写实验报告的不二法门。


遗憾的是,计算机系的似乎有把实验报告写得很糟烂的传统?那么一页半的代码
和结果,你是打算让我相信,你从头开始敲代码,敲到最后,然后编译就通过了
么。那么,你一定不知道,代码不是从上向下写的。从上向下写,就像画蒙娜丽
莎的时候,把这杰作分成1024行,然后一行一行从下向下扫描。写代码,也是先
写眼睛,再写耳朵,可能再改下眼睛...这样的顺序。即使很短的代码也是这样。


所以,在不断的修改中,我们需要始终保证我们的代码还是按我们预想的方式工
作的。这就需要测试。


说到这里,我总会回想起小时候做航模,没有耐心等到胶完全固化就想去测试机
翼和机身连接的强度。好在,软件从写出来到测试,快得多。


前两天写了500来行java,有如下体会,与测试和观察软件的行为有关。


在软件从第一行开始成长的过程中,我这样确定它,此刻,仍然符合我所想的。


1. log - 我要知道一切,用户能看到的,用户看不到的,你所想的。


这次我没有用log4j。上次用了,这次最初以为这样部署起来容易一些,现在后
悔了。最初在软件的规模就应该有个估计,这种规模对于我来说,就应该有log
了。


log4j与System.out.println()相比,一个明显的好处是,在发布的时候,只要把
输出信息的级别在配置文件中修改一下,就可以阻止所有的调试信息输出。而
System.out.println()一行行注释起来,就容易落了这个丢了那个。


即使你有那个精力,用在看碟打游戏上岂不是更好。自动化那些非创造性的工
作,始终都我们追求的一个目标。


log非常重要的另一个原因,我和典同学一样,很少使用dbg这类东西,也不用
IDE的messages,而是喜欢自己输出到控制台之类的。log4j的输出可以与正常的
用户会看到的输出分开。


2. 测试 - 不停地考验


就像搭积木一样,我们总是要在可能不稳固的地方设置一两个测试点,输出一些
东西,让我们确认,到这一层,还是稳妥的。


不要"相信"代码是正确的。如果它是正确的,那么,运行一下证实,这花不了多
少时间;如果它不正确,你需要第一时间知道真相。在错误的基础之上继续写,
接下来付出的代价,除了失败的经验以外,没有任何收益。Tom Hagen说:"我的
代理人希望第一时间知道坏消息。"


放心地一遍遍测试吧,你的代码不会因此而认为你不相信它的--只有上帝(和女
人?)才有这个规定。


每当修改一小段代码,就应该测试一下。根据回归测试的原则,任何一处修改,
都可能让原来正确的东西变成错误的。所以,每个修改都应该测试。


仍然是那个原则,不要"相信"代码是正确的。下面这个例子,就是我犯的错误。
当时我想,这么简单的修改,就不必测了。直到十多分钟以后,发现这家伙的行
为不正常。可是这个醉汉已经又走出好远了。


错误的代码:
: while (i.hasNext())
: {
: String current = i.next();
: if(i.next()==null || value==null)
: {
: return false;
: }
: else if(eval(i.next()).equals(eval(value)) )
: {
: return true;
: }
: }


正确的代码:


: while (i.hasNext())
: {
: String current = i.next();
: if(current==null || value==null)
: {
: return false;
: }
: else if(eval(current).equals(eval(value)) )
: {
: return true;
: }
: }


如果我当时做了回归测试,就可以马上发现错误的代码中副作用-- i.next()不
仅取出了值,也移动了"指针"。这样的错误在C/C++中也常发生。


3. 一个小技巧


如果我们在回归测试中,不断地观察程序的输出,就需要集中注意力找"不同"。
如果输出是一大摊的话,那就更是麻烦,可能还要翻页才行。


这也可以自动化。


我们可以先把正确的输出写下来,文件名叫 expect.txt 吧。


然后我们把每次的输入写下来,文件名叫 input.txt 吧。
如果你有多组输入,多整几个文件。


然后我们这样运行我们的程序:


proc.exe < input.txt > output.txt


如果你有多组输入,就做个批处理,这样:


proc.exe < input1.txt > output.txt
proc.exe < input2.txt > output.txt
proc.exe < input3.txt > output.txt


现在,可以看看程序是否按我们期待的运行了,基本不需要眼睛:


diff output.txt expect.txt


如果啥输出也没有,那就是它俩完全相同。


这与自然科学研究的技术路线差不多。


先做实验,或者观察世界,得到expect.txt;


然后假说,得到proc.exe;


最后看看proc.exe运行的结果output.txt与实验结果expect.txt是否一样。


如果结果好,那么写论文;如果结果不好,有些人会假装没看到,或者...反正
修改实验结果是不行的,因为还有别人也会做实验。


4. 更好的东西


上述小技巧可以写成一个批处理,改会代码,编译,然后就跑一遍这个批处理。
最初每次期望与结果都是不一样的,也就是还不成。再改再改,后来,diff不出
声了。


大功告成。


这个批处理的路子早就被俺们这个学科的古人发现了,并且写成了工具,叫做
JUnit。


还有其他语言的版本 cppUnit 啥的。


牛人总是N多,所以,我们能想到,必然早就被牛人实现了。就像,剧毒之物五
步以内,必有解药。如果你嫌断肠草药性太烈呢,就自己去动手改进吧。
我现在想开发一个深度学习骨增量诊断及治疗方案设计的系统。实现的功能为输入患者的ct及要种植的位后,医生通过可视化工具移动、旋转平面,确认咬合平面,输出该位是否骨损,如果有,是什么类型的(按照turheyden分类和misch分类),同时提取出骨位的颌骨骨段,对其进行数学特征的提取,将turheyden分类+misch分类+数学特征+大语言模型提取的患者病历文本信息作为输入特征,输入到训练好的知识图谱诊断模型中,得出诊断及治疗方案的规划。我对这个任务的拆解如下:1.ct的预处理及重采样,略。2.使用预训练模型对种植相关的解剖标志进行分割:上颌骨、下颌骨、32颗齿(各为1类)分别分割。3.使用程序统计分割出的齿,根据fdi位编码统计处的位点,输出;4.使用程序判断医生输入的位点是否属于位点,若是,则医生输入位点的邻(可通过代码进行判断,如13相邻的是12和14)采用轴对齐包围盒即立方体边界框(其z轴垂直于用户确认的咬合平面),从该齿分割掩膜中提取所有属于目标位的体素点,计算其最小/最大坐标值,共6个,构成bbox,再根据经验值对不同的位施加不同的扩散范围。对骨损位点近中、远中两个相邻位进行均此操作后去除bbox内的像素点及其掩膜,再对剩余像素执行“去除不相接的部分”,保留下骨损骨段(我有此想法是因为geomagic等软件有此类操作,但是再这个程序中去除不相接的部分不是手动而是自动,那么如何判断剩余三段骨哪个是骨损骨段?这是个问题)。5.turheyden分类模型:对分割出的骨损骨段识别其turheyden分类,此为一个识别任务,医生手动标注其属于哪一类。6.misch分类:基于分割出的骨段的hu值进行分割,若有已有算法则按照已有算法,没有则再进行一个识别任务。7.数学特征提取:对分割出的骨段提取数学特征,如几何、拓扑学特征。8.文本处理:Biobert提取病历中的关键实体 9.知识图谱构建:将两种分类信息、文本、数学特征、患者的治疗方案信息通过关系抽取模型进行图谱融合,采用Neo4j+pytorch geometric实现实时图谱动态更新。请分析我整个架构的合理性,并给出一个你认为最优的现实方案。并且我在其中也提到了一些问题,请解决或告诉我该去查找哪些东西 请用自然语言回答而非代码
03-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值