33、Moles框架助力单元测试及所有权类型编码探索

Moles框架与所有权类型编码探析

Moles框架助力单元测试及所有权类型编码探索

1. Moles框架操作语义

Moles框架提供了一系列操作,用于处理moles的附加和分离,以及调用和返回操作。这些操作可以对程序的全局状态进行修改,并在测试中模拟各种场景。
- 附加和分离moles
- 当指令为 mole M := (y, N) 时,将 M 映射到 (y, N) 添加到全局状态 H 中。
- 当指令为 unmole M 时,从全局状态 H 中移除 M
- 当指令为 mole (x, M) := (y, N) 时,将 (x, M) 映射到 (y, N) 添加到全局状态 H 中。
- 当指令为 unmole (x, M) 时,从全局状态 H 中移除 (x, M)
其形式化表示如下:

stepP (H, R ++ (PC , L))
where (instr P (PC ) = mole M := (y, N))
= (H ⊕{M →(y, N)}, R ++ (PC + 1, L))
stepP (H, R ++ (PC , L))
where (instr P (PC ) = unmole M)
= (H \ M, R ++ (PC + 1, L))
stepP (H, R ++ (PC , L))
where (instr P (PC ) = mole (x, M) := (y, N))
= (H ⊕{(x, M) →(y, N)}, R ++ (PC + 1, L))
stepP (H, R ++ (PC , L))
where (instr P (PC ) = unmole (x, M))
= (H \ (x, M), R ++ (PC + 1, L))
  • 调用和返回操作
    • 当指令为调用操作时,会检查是否附加了特定实例的mole、与实例无关的mole或没有附加mole,并添加新的栈帧。
    • 当指令为返回操作时,会移除当前栈帧,并将结果值存储在先前的栈帧中。
      其形式化表示如下:
stepP (H, R ++ (PC , L))
where (instr P (PC ) = _ := call M(y1, . . . , yn))
=
⎧
⎨
⎩
(H, R ++ (PC , L) ++ f One
v,N) if (L(y1), M) →(v, N) ∈H
(H, R ++ (PC , L) ++ f All
v,N) if M →(v, N) ∈H
(H, R ++ (PC , L) ++ f )
otherwise
where the new frame fv,N, fN or f is defined as follows:
f One
v,N = (startP (N), {arg1 →v} ⊕{argi →L(yi) : 2 ≤i ≤n})
f All
v,N = (startP (N), {arg1 →v} ⊕{argi+1 →L(yi) : 1 ≤i ≤n})
f
= (startP (M), {argi →L(yi) : 1 ≤i ≤n})
stepP (H, R ++ (PC , L) ++ (PC ′, L′))
where (instr P (PC ) = x := call _(_)
∧
instrP (PC ′) = ret y)
= (H, R ++ (PC + 1, L ⊕{x →L′(y)}))
2. SharePoint单元测试案例研究

在SharePoint应用程序的单元测试中,使用Moles和Pex工具可以有效地解决传统测试中存在的问题。
- SharePoint单元测试的挑战
- SharePoint Foundation提供了一个综合内容管理平台,但在进行单元测试时存在诸多困难。
- 由于需要连接到实时的SharePoint站点,大多数过去编写的“单元测试”实际上是集成测试,运行时间长,无法满足单元测试的快速、单一组件测试和完全可重现的目标。
- SharePoint对象模型中的许多类是密封类型,具有非公共构造函数,不允许测试人员注入虚假服务实现。
- 待测试代码
以下是一个简单的SharePoint应用程序的 UpdateTitle 方法,用于更新 SPListItem 实例中的字段:

public void UpdateTitle(SPItemEventProperties properties) {
    using (SPWeb web = new SPSite(properties.WebUrl).OpenWeb()) {
        SPList list = web.Lists[properties.ListId];
        SPListItem item = list.GetItemById(properties.ListItemId);
        string contentType = (string)item["ContentType"];
        if (contentType.Length < 5)
            throw new ArgumentException("too short");
        if (contentType.Length > 60)
            throw new ArgumentOutOfRangeException("too long");
        if (contentType.Contains("\r\n"))
            throw new ArgumentException("no new lines");
        item["Title"] = contentType;
        item.SystemUpdate(false);
    }
}

该方法被封装在 ContentTypeItemEventReceiver 类中:

public class ContentTypeItemEventReceiver
    : SPItemEventReceiver {
    public override void ItemAdded(
        SPItemEventProperties properties) {
        this.EventFiringEnabled = false;
        this.UpdateTitle(properties);
        this.EventFiringEnabled = true;
    }
    ...
}
  • 使用Moles进行参数化单元测试
    • 选择合适的 contentType 对于测试 UpdateTitle 方法的实现至关重要,因为不同的值会导致程序执行不同的代码路径。
    • Pex工具可以自动系统地找到相关的测试输入。它在执行待测试代码时,会监控.NET代码执行的每一条指令,记录程序检查的条件及其与测试输入的关系,并将这些信息提供给约束求解器,以生成触发不同代码路径的新输入。
      以下是参数化单元测试的代码:
[PexMethod]
public void UpdateTitle(int listItemId, object contentType) {
    // arrange
    string url = "http://foo";
    Guid listId = new Guid();
    var properties = new MSPItemEventProperties {
        WebUrlGet = () => url,
        ListIdGet = () => listId,
        ListItemIdGet = () => listItemId
    };
    bool titleSet = false;
    MSPSite.ConstructorString = (site, _url) => {
        new MSPSite(site) {
            OpenWeb = () => new MSPWeb {
                Dispose = () => { },
                ListsGet = () => new MSPListCollection {
                    ItemGetGuid = id => {
                        Assert.IsTrue(listId == id);
                        return new MSPList {
                            GetItemByIdInt32 = itemId => {
                                Assert.IsTrue(listItemId == itemId);
                                return new MSPListItem {
                                    InstanceBehavior =
                                        MoleBehaviors.DefaultValue,
                                    ItemGetString = name => {
                                        Assert.IsTrue("ContentType" == name);
                                        return contentType;
                                    }, // ItemGetString
                                    ItemSetStringObject = (name, value) => {
                                        Assert.IsTrue("Title" == name);
                                        Assert.IsTrue(contentType == value);
                                        titleSet = true;
                                    } // ItemSetStringObject
                                }; // new MSPListItem
                            } // GetItemByIdIn32
                        }; // new MSPList()
                    } // ItemGetGuid
                } // new MSPListCollection
            } // new MSPWeb
        }; // new MSPSite
    }; // MSPSite.New
    // act
    var target = new ContentTypeItemEventReceiver();
    target.UpdateTitle(properties);
    // assert
    Assert.IsTrue(titleSet);
}
  • 评估
    • 让Pex探索参数化单元测试代码和待测试代码,在七秒内生成了六个非参数化单元测试。
    • 除了两个测试用例失败(一个导致 NullReferenceException ,另一个导致 InvalidCastException )外,其他测试用例都通过。
    • 生成的测试套件覆盖了代码中的所有显式分支和基本块。
3. 相关工作比较
工具/方法 特点 缺点
TypeMock Isolator 商业工具,提供强大API用于陈述期望、施加验证检查和添加注释 不是专注于提供基本或系统的绕行功能,而是模拟框架
JMockit 适用于Java的绕行框架 同TypeMock Isolator,是模拟框架,不提供轻量级基于委托的绕行功能
基于AOP的方法 可用于实现方法调用的绕行和将系统测试转换为单元测试 通用AOP语言的语法和语义增加了单元测试语义的复杂性
自动将系统测试转换为单元测试的方法 基于现有系统测试,采用捕获和重放方法 不提供在用户编写的测试代码中附加自定义行为到绕行环境交互的简单机制
4. 所有权类型在Java中的编码
4.1 所有权类型简介

所有权类型可以描述程序源代码中堆的拓扑结构,有多种变体,如上下文参数化、Universes、所有权域、OGJ等,具有许多实际应用,包括防止数据竞争、并行化、实时内存管理和实施架构约束等。

4.2 编码方法

通过扩展先前确定的所有权类型与参数类型之间的关系,将所有权类型编码为标准泛型Java。将当前对象的所有权上下文( this This 上下文)视为标准类型参数,并通过存在量化将其外部隐藏。

4.3 编码贡献
  • 展示了所有权类型与参数和存在类型之间的联系。
  • 使用Java类型系统的模型形式化编码,并证明其正确性和强制所有权层次结构。
  • 利用编码开发了用于所有权类型和宇宙类型的轻量级编译器,每个编译器仅需一天即可实现。
5. 总结

Moles框架为单元测试提供了一种有效的隔离机制,通过绕行环境依赖方法的调用,使得单元测试更加独立和可自动化分析。在SharePoint应用程序的单元测试案例中,Moles和Pex工具的结合显著提高了测试效率。同时,将所有权类型编码为标准Java类型系统的方法,为所有权类型的实现和应用提供了新的思路和解决方案。未来,可以进一步完善Moles框架,简化模型编写,以及探索所有权类型编码的更多应用场景。

Moles框架助力单元测试及所有权类型编码探索

6. Moles框架与其他工具的优势对比

Moles框架与传统的模拟框架相比,具有明显的优势,以下是详细对比:
| 对比项 | Moles框架 | 传统模拟框架 |
| ---- | ---- | ---- |
| 实现方式 | 基于.NET委托,提供轻量级绕行功能 | 通常是模拟框架,提供强大API用于期望陈述和验证检查 |
| 状态维护 | 利用C#闭包捕获变量,方便编写有状态模型 | 简单的捕获 - 重放结构,难以在方法调用间维护和操作状态 |
| 代码复杂度 | 便于编写轻量级和可重用模型 | 可能因复杂的API和注释增加代码复杂度 |
| 测试生成 | 配合Pex可快速自动生成测试用例 | 可能需要手动编写大量测试用例 |

7. Moles框架操作流程
graph TD;
    A[开始] --> B[判断指令类型];
    B --> C{是否为mole操作};
    C -- 是 --> D[执行mole附加或分离操作];
    C -- 否 --> E{是否为call操作};
    E -- 是 --> F[检查mole附加情况并添加栈帧];
    E -- 否 --> G{是否为ret操作};
    G -- 是 --> H[移除当前栈帧并存储结果值];
    G -- 否 --> I[结束];
    D --> I;
    F --> I;
    H --> I;
8. SharePoint单元测试流程
graph TD;
    A[开始] --> B[选择合适的contentType];
    B --> C[使用Pex生成测试输入];
    C --> D[执行参数化单元测试];
    D --> E[Pex探索测试代码和待测试代码];
    E --> F[生成非参数化单元测试];
    F --> G[运行测试用例];
    G --> H{测试是否通过};
    H -- 是 --> I[记录通过情况];
    H -- 否 --> J[记录失败信息];
    I --> K[检查是否覆盖所有分支和基本块];
    J --> K;
    K -- 是 --> L[测试完成];
    K -- 否 --> M[调整测试输入重新测试];
    M --> C;
9. 所有权类型编码的应用场景

所有权类型编码在多个领域有重要的应用,以下是具体的应用场景列表:
- 并行化 :通过明确对象的所有权关系,避免数据竞争,提高并行程序的性能和安全性。
- 并发编程 :确保不同线程对对象的访问不会产生冲突,保证程序的正确性。
- 内存管理 :帮助管理对象的生命周期,提高内存使用效率。
- 安全领域 :实施架构约束,防止非法访问和数据泄露。

10. 未来展望
  • Moles框架 :可以进一步添加功能,简化有经验的测试人员或开发人员编写模型的过程,提高框架的易用性。
  • 所有权类型编码 :探索更多的所有权类型变体和扩展的编码方法,扩大其应用范围,为更多领域的软件开发提供支持。
11. 结论

Moles框架和所有权类型编码在软件开发和测试领域都具有重要的价值。Moles框架通过绕行环境依赖方法,实现了单元测试的有效隔离,提高了测试效率和可自动化分析性。所有权类型编码则为所有权类型的实现和应用提供了新的途径,展示了其与标准类型系统的紧密联系。在未来的软件开发中,这两种技术有望发挥更大的作用,推动软件质量和开发效率的提升。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值