读书笔记——软件架构的可测试性
基本概念:
软件的可测试性是指在一定时间和成本前提下,进行测试设计、测试执行以此来发现软件的问题,以及发现故障并隔离、定位其故障的能力特点。各种组织对可测试性有不同的定义,我认为其本质是相通的,都是在说一个软件系统能够被测试的难易程度,或者是说软件系统可以被确认的能力。
测试的重要性:
软件测试一般是用来测试软件中的BUG,以此去验证产品是否符合它的原本需求,使用最小的成本和工作量来验证软件的质量,使得软件的功能(functionality)和质量(quality)达到平衡。
软件测试在一般情况下,花在测试上的成本近40%,因为如果一个大型软件出现故障,则会造成更为巨大成本的损失,而如果能够子啊体系架构层面提高软件的可测试性,则收益巨大。
软件测试是被设计和实现出来的。软件的可测试性需要被明确地设计,根据设计出来的可测试性方案在整个研发流程中进行测试,而不是在研发完全结束后或这中间的某一个阶段进行测试。
可测试性场景:
刺激源:可能由不同角色发起(开发者,单元、继承测试人员,用户等等)
刺激:系统开发达到某个阶段或者完成
响应:可以进行测试并且有测试结果/不可以进行测试
涉及到的战术策略:
黑盒测试:记录/回放;自动化/半自动化测试;吧接口和实现分离开;不同排序算法,使用相同的接口;提供专用的测试路径
白盒测试:内部监控;IDE提供的断点等调试工具
常用测试工具:Appiun(APP UI测试);Selenium(Web UI测试);JMeter(接口测试、性能测试);UTF(功能测试);Silk Test(功能测试);Windbg(Win调试);Steho(安卓调试);LoadRunner(性能测试)
战术案例应用:
所以为了提高软件的可测试性,首先我们应该遵守“高内聚,低耦合”的设计原则。
单一职责原则,一个类只负责一项职责。它是我们在写代码时必须遵守的原则,他能提高类的可读性,提高系统的可维护性。变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
1 class Clothes{ 2 public void winter(String clothes){ 3 System.out.println(clothes+"冬天穿"); 4 } 5 6 public void summer(String clothes){ 7 System.out.println(clothes+"夏天穿"); 8 } 9 } 10 11 public class Client{ 12 public static void main(String[] args){ 13 Clothes c = new Clothes(); 14 c.winter("羽绒服");
c.summer("短袖");
18 }
记录回放是指捕获跨接口的信息,并将其作为测试专用软件的输入。在正常操作中操作中跨一个接口的信息保存在某个存储库中,它代表来自一个组件的输出和传到一个组件的输入。记录该信息使得能够生成对其中一个组件的测试输入,并保存用于以后比较测试输出。
1 public class Control 2 { 3 List<ICommand> onCommands; 4 Stack<ICommand> undoCommands; 5 Stack<ICommand> redoCommands; // 记录前一个命令, 便于 undo 6 7 public Control() 8 { 9 onCommands = new List<ICommand>(); 10 undoCommands = new Stack<ICommand>(); 11 redoCommands = new Stack<ICommand>(); 12 } 13 14 public void SetCommand(int slot, ICommand onCmd) 15 { 16 onCommands[slot] = onCmd; 17 } 18 19 public void OnButtonWasPressed(int slot) 20 { 21 if (onCommands[slot] != null) 22 { 23 onCommands[slot].execute(); 24 undoCommands.Push(onCommands[slot]); 25 } 26 } 27 28 public void UndoButtonWasPressed() // 撤销,此处用 stack 后进先出的特性 29 { 30 if (undoCommands.Count > 0) 31 { 32 ICommand cmd = undoCommands.Pop(); 33 redoCommands.Push(cmd); 34 cmd.undo(); 35 } 36 } 37 38 public void RedoButtonWasPressed() 39 { 40 if(redoCommands.Count > 0) 41 { 42 ICommand cmd = redoCommands.Pop(); 43 undoCommands.Push(cmd); 44 cmd.execute(); 45 } 46 } 47 }
将接口与实现分离允许实现的代替,以支持各种测试目的。占位实现允许在缺少被占用的组件时,对系统的剩余部分进行测试。用一个组件代替某个专门的组件能够使被代替的组件充当系统剩余部分的测试工具。有了标准的接口,进行有效的隔离,能够极大程度的减少测试员的工作量,模块化测试,单元测试极大地减少了测试过程中用例的原则,如果没有接口隔离,不管用的是极值分析法还是等价划分法,都对测试员的工作造成了极大的负担,相对而言,单元测试更加简化了工作量,让白盒测试过程中的逻辑复杂度降低了不少。
1 public interface ImageLoader { 2 3 /** 4 * 初始化ImageLoader 5 * @param appContext ApplicatonContext 6 */ 7 void init(@NonNull Context appContext); 8 9 /** 10 * 展示图片 11 * @param targetView 12 * @param uri 13 * @param listener 14 */ 15 void displayImage(@NonNull ImageView targetView, @NonNull Uri uri, @Nullable LoadListener listener); 16 17 /** 18 * 取消图片展示 19 * @param targetView 20 */ 21 void cancelDisplay(ImageView targetView); 22 23 /** 24 * 销毁ImageLoader, 回收资源 25 */ 26 void destroy(); 27 28 }