L`Bat的博客

为者常成 行者常至

testable_code

概念

集成测试对用户的交互行为感兴趣,而单元测试往往仅专注于一小段代码

软件IC是人们在讨论可复用性和基于组件的开发时最喜欢使用的比喻。意思是软件组件应该就像集成电路一样进行组合,这只有在你使用的组件已知是可靠地时候才能行之有效。
  芯片在设计时就考虑了测试——不只是在工厂,在安装时,而且也是在部署现场进行测试。更加复杂的芯片和系统可能还拥有完整的Built-In-SelfTest(BIST)特性,用于在内部运行某种基础级的诊断;或是拥有Test Access Mechanism(TAM),用以提供一种测试装备,允许外部环境提供激励,并收集来自芯片的响应。

untestable的代码

  • 隐藏的输入
  • 依赖过多,构建麻烦
  • 耦合度过高
  • 非单一职责
  • 显式的依赖DB NETWORK 未测试的类等(应该依赖接口或者mock)

验收标准

  • 覆盖率高(路径被遍历)
  • 圈复杂度低
  • 每个输入有唯一的输出
  • 过去的系统状态和变量可见,或在运行中可查询(例如:事务日志)
  • 软件的可测试性特征主要表现是设立观察点、控制点、观察装置、驱动装置、隔离装置

pricinple

  • 设计之初就考虑了测试模式
  • 承认单元测试不是万能的,只保证部分正确
  • 尽量少的依赖

    First rule of writing testable code to me is having a good dependency management within the code base, or at least having the least possible dependencies in your object/module relations

  • 执行路径过多(每个执行路径是不同的代码,都该有个测试与之对应)

    应该由单个方法来决定if的条件
    嵌套的panduan
    == null
    > 2

  • 可控性指系统的状态可受外部控制改变,而不是由内部模块自发的完成

    测试环境不可以等待每天晚上1点这个时刻

  • 可观测性

    再次,没有必要的描述如何判断哪些文件/数据被GC掉了。无法观测到执行结果集带来的后果是无法精确的预期测试结果。

  • 参数的热设定
  • 系统使用信息统计

NOT testable

  1. 包含不确定的因素
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public static string GetTimeOfDay()
    {
    DateTime time = DateTime.Now;
    if (time.Hour >= 0 && time.Hour < 6)
    {
    return "Night";
    }
    if (time.Hour >= 6 && time.Hour < 12)
    {
    return "Morning";
    }
    if (time.Hour >= 12 && time.Hour < 18)
    {
    return "Afternoon";
    }
    return "Evening";
    }
  • 依赖了系统时间
  • 隐藏了输入(依赖)
  • 相同测试不同输出

测什么

CORRECT

实现

  1. 完全的TDD
  2. 实现-> 测试 -> 重构以更testable -> 更多测试 -> 重构测试

todo

  • 监视圈复杂度

cache

Vary

告知缓存服务器:在计算”决定是否要缓存的hash值”时,要把哪些头考虑进去

设计模式:适配器模式

定义

适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)

形象

  • 手机充电口

参与角色

  • Adaptee
  • AbstractAdapter 固定的一端确定
  • ConcreteAdapter
  • Target (可以使调用方,也可以是被调用方)

案例

  • springmvc中对handler的处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    //AbstractAdapter
    public interface HandlerAdapter {
    boolean supports(Object handler);
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    }
    //Adaptee A
    public interface Controller {
    ModelAndView handleRequest(HttpServletRequest, HttpServletResponse);
    }
    //ConcreteAdapter A
    public class HttpRequestHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
    return (handler instanceof HttpRequestHandler);
    }
    @Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {
    ((HttpRequestHandler) handler).handleRequest(request, response);
    return null;
    }
    }
    //Adaptee B
    interface Servlet {
    public void service(ServletRequest req, ServletResponse res)
    }
    //ConcreteAdapter B
    public class SimpleServletHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
    return (handler instanceof Servlet);
    }
    @Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {
    ((Servlet) handler).service(request, response);
    return null;
    }
    }

spring mvc处理http请求的过程

spring框架

mvc处理流程图

介绍servlet
实际上是遵循了servlet规范

  1. doPost / doGet 调用DispatcherServlet.doService
  2. request可以存取attribute, 存储原有attrs
  3. 设置常用attrs
    • context
    • localeResolver 修改点
    • themeResolver 修改点
    • 处理并设置FlashMap(FlashMap是不同请求间共享数据的方式)
  4. 处理multipart

    • 使用multipartResovler处理
    • 如果错误写入错误到attrs(attrs是MultiValueMap)
  5. List<HandlerMapping>中找第一个HandlerExecutionChain 修改点

  6. 如果是Get请求, 处理302
    • ETag
    • lastModified
    • 设置302
    • 写回Last-Modified
  7. 请求SimpleUrlHandlerMapping(求改点)得到HandlerExcutionChain
    • 如果是handler是String,那么去context里拿到Bean作为handler
    • 在Mapping中获得匹配的(Interceptor.matches)来加入Chain
  8. 分配请求到HandlerExecutionChain
    • 调用HandlerInterceptor.preHandle, 一次调用,有false结束
    • 调用HandlerInterceptor.handle, 获得MoSimpleControllerHandlerAdapterdelAndView; handle内部
    • 调用HandlerInterceptor.postHandle

      常用实现 SimpleControllerHandlerAdapter
      每个Handler的实现自己提供Adapter, 因为handler是个object

  1. 如果发生Exception | Throwable,调用preHandle已经返回true的Interceptor.triggerAfterCompletion 修改点
  2. 清理multipart 修改点

其他