【提示】

1. 阅读文本前希望您具备如下知识:了解单元测试,了解Dynamic,熟悉泛型(协变与逆变)和Lambda,熟悉.NET Framework提供的
Action与Func委托。
2.如果您对单元测试无兴趣请止步。

3.本文将使用一些我自己的测试公共代码,位于https://idletest.codeplex.com/,此处亦非常欢迎来访。

4.关于本人之前单元测试的文章可参阅

在Visual Studio 2012使用单元测试》、

VS2012 单元测试之泛型类(Generics Unit Test)》、

VS2012 Unit Test —— 我对接口进行单元测试使用的技巧

【修改IdleTest】

为了适应本次单元测试的编码,对IdleTest进行了一些更新,本文只描述更新部分,具体源码请移步https://idletest.codeplex.com/

1.重构TestCommon类的ArrayEqual方法,变成了

        #region Equal
/// <summary>
/// 判断两个数组项相等(顺序必须一致),对数组项使用"Equal方法"校验,
/// 如果非CTS类型(即自定义),则应在使用本方法前对Equal方法进行重载
/// </summary>
public static bool ArrayEqual(Array array1, Array array2)
{
bool isCountEqual = CollectionCountEqual(array1, array2);
if (!isCountEqual || array1 == null || array2 == null)
{
return isCountEqual;
} for (int i = ; i < array1.Length; i++)
{
if (!object.Equals(array1.GetValue(i), array2.GetValue(i)))
{
return false;
}
} return true;
} /// <summary>
/// 判断两个集合项相等(顺序必须一致),对集合项使用"Equal方法"校验,
/// 如果非CTS类型(即自定义),则应在使用本方法前对Equal方法进行重载
/// </summary>
public static bool ListEqual(IList list1, IList list2)
{
bool isCountEqual = CollectionCountEqual(list1, list1);
if (!isCountEqual || list1 == null || list2 == null)
{
return isCountEqual;
} for (int i = ; i < list1.Count; i++)
{
if (!object.Equals(list1[i], list2[i]))
{
return false;
}
} return true;
} /// <summary>
/// 判断两个集合项相等(顺序必须一致),对集合项使用"Equal方法"校验,
/// 如果非CTS类型(即自定义),则应在使用本方法前对Equal方法进行重载
/// </summary>
public static bool CollectionEqual(object collection1, object collection2)
{
if (collection1 == null && collection2 == null)
{
return true;
} if (collection1 is Array && collection2 is Array)
{
return ArrayEqual(collection1 as Array, collection2 as Array);
} if (collection1 is IList && collection2 is IList)
{
return ListEqual(collection1 as IList, collection2 as IList);
} return false;
} /// <summary>
/// 验证两个集合的长度是否一致
/// </summary>
/// <param name="collection1">要判断的集合1</param>
/// <param name="collection2">要判断的集合2</param>
/// <returns>长度相等(两个集合为null或者长度为0以及一个为null另一个长度为0均认为相等)
/// 返回true,否则返回false</returns>
public static bool CollectionCountEqual(ICollection collection1, ICollection collection2)
{
if ((collection1 == null || collection1.Count < )
&& (collection2 == null || collection2.Count < ))
{
return true;
}
else if ((collection1 == null || collection1.Count < )
|| (collection2 == null || collection2.Count < ))
{
return false;
} return collection1.Count == collection2.Count;
} #endregion

2. AssertCommon类新增方法如下

        /// <summary>
/// 断言为Empty
/// </summary>
/// <typeparam name="TParameter1">方法参数类型1</typeparam>
/// <typeparam name="TParameter2">方法参数类型2</typeparam>
/// <typeparam name="TReturn">方法返回类型</typeparam>
/// <param name="action">调用的方法</param>
/// <param name="args1">需断言的参数集1</param>
/// <param name="args2">需断言的参数集2</param>
/// <param name="funcs">测试的方法集合</param>
/// <param name="assertNull">是否断言为空</param>
public static void AssertEmpty<TParameter1, TParameter2, TReturn>(
TParameter1[] args1, TParameter2[] args2, bool assertEmpty = true, params Func<TParameter1, TParameter2, TReturn>[] funcs)
{
AssertHandle<TParameter1, TParameter2, TReturn>((TReturn actual) =>
{
AssertEmpty<TReturn>(actual, assertEmpty);
}, args1, args2, funcs);
}

【入正题】

如标题所说,我现在有如下无返回值以及使用Action和Func作为参数的几个方法

    public class UtilityCommon
{
#region Foreach Handle
/// <summary>
/// 进行遍历
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="array">遍历的集合</param>
/// <param name="action">遍历到每一项执行的方法</param>
/// <param name="breakBeforeFunc">跳出循环的方法(action执行前)</param>
/// <param name="breakAfterFunc">跳出循环的方法(action执行后)</param>
public static void ForeachHandle<T>(IEnumerable<T> array, Action<T> action,
Func<T, bool> breakBeforeFunc, Func<T, bool> breakAfterFunc)
{
if (array == null || action == null)
{
return;
} foreach (T item in array)
{
if (breakBeforeFunc != null && breakBeforeFunc(item))
{
break;
} action(item);
if (breakAfterFunc != null && breakAfterFunc(item))
{
break;
}
}
} /// <summary>
/// 进行遍历
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="array">遍历的集合</param>
/// <param name="action">遍历到每一项执行的方法</param>
public static void ForeachHandle<T>(IEnumerable<T> array, Action<T> action)
{
ForeachHandle<T>(array, action, null, null);
} /// <summary>
/// 进行遍历,如果迭代器中的项不为T类型,则跳过不执行操作(action)
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="array">遍历的集合</param>
/// <param name="action">遍历到每一项执行的方法</param>
/// <param name="breakBeforeFunc">跳出循环的方法(action执行前)</param>
/// <param name="breakAfterFunc">跳出循环的方法(action执行后)</param>
public static void ForeachHandle<T>(IEnumerable array, Action<T> action,
Func<T, bool> breakBeforeFunc, Func<T, bool> breakAfterFunc)
{
if (array == null || action == null)
{
return;
} foreach (var item in array)
{
if (item is T)
{
T t = (T)item;
if (breakBeforeFunc != null && breakBeforeFunc(t))
{
break;
} action(t);
if (breakAfterFunc != null && breakAfterFunc(t))
{
break;
}
}
}
} /// <summary>
/// 进行遍历,如果迭代器中的项不为T类型,则不执行操作(action)
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="array">遍历的集合</param>
/// <param name="action">遍历到每一项执行的方法</param>
public static void ForeachHandle<T>(IEnumerable array, Action<T> action)
{
ForeachHandle<T>(array, action, null, null);
}
#endregion
}

需要进行测试的代码

这正体现着单元测试中三个难点:

(1)void返回类型。由于没有返回值,所以只能模拟方法内部操作的需求进行测试。作为无返回值的方法,其肯定改变了外部的一些信息,否则该方法基本上没有意义,这就使得其具有可测试性。比如下面的代码,将方法对外界的影响转变成了对“arrayActual”这个变量的影响,使用该方式需要注意闭包产生影响。

(2)以委托对象作为参数,由于调用的委托未知,故而对其做单元测试很难完全做到客观上绝对的100%覆盖率,尽管用VS运行覆盖率分析时达到了100%。这个大家可以看我的代码找到未覆盖的模块,很容易看出来的,呵呵,不是我预留,而是我遵从VS的覆盖分析结果就懒得去改罢了。

(3)方法有重载时,消除重复测试代码。我的做法一般是把所有的重载测试代码的数据构造提炼成一个方法,然后在各测试中以实际调用的方法作为参数传入,能做到这步田地真的非常感谢dynamic。

具体关于我对以上三点的做法,请看如下测试代码

    [TestClass()]
public class UtilityCommonTest
{
/// <summary>
///ForeachHandle 的测试
///</summary>
public void ForeachHandleTestHelper(dynamic actionTest, bool hasBreakBefore = false, bool hasBreakAfter = false)
{
bool notBreak = !hasBreakAfter && !hasBreakBefore; IEnumerable<int> array = new int[] { , , , , };
List<int> arrayActual = new List<int>();
Action<int> action = p => arrayActual.Add(p);
Func<int, bool> breakBeforeFunc = null; // TODO: 初始化为适当的值
Func<int, bool> breakAfterFunc = null; // TODO: 初始化为适当的值
//UtilityCommon.ForeachHandle<int>(array, action, breakBeforeFunc, breakAfterFunc);
if (notBreak)
{
actionTest(array, action);
}
else
{
actionTest(array, action, breakBeforeFunc, breakAfterFunc);
} AssertCommon.AssertEqual(array, arrayActual); //************************************************************************************* arrayActual = new List<int>();
AssertCommon.AssertEmpty<IEnumerable<int>, List<int>, List<int>>(
new IEnumerable<int>[] { null, new int[] { }, new List<int>() },
new List<int>[] { arrayActual },
true,
(pIn1, pIn2) =>
{
//UtilityCommon.ForeachHandle<int>(pIn1, p => pIn2.Add(p));
if (notBreak)
actionTest(pIn1, new Action<int>(p => pIn2.Add(p)));
else
actionTest(pIn1, new Action<int>(p => pIn2.Add(p)), breakBeforeFunc, breakAfterFunc); return pIn2;
}); if (notBreak)
return; //*************************************************************************************
// If Has Break Function breakBeforeFunc = p => p > ;
breakAfterFunc = p => p > ; arrayActual = new List<int>();
actionTest(array, action, breakBeforeFunc, null);
AssertCommon.AssertEqual<IList>(new int[] { , , , }, arrayActual); arrayActual = new List<int>();
actionTest(array, action, null, breakAfterFunc);
AssertCommon.AssertEqual<IList>(new int[] { , , , }, arrayActual); arrayActual = new List<int>();
breakBeforeFunc = p => p > ;
actionTest(array, action, breakBeforeFunc, breakAfterFunc);
AssertCommon.AssertEqual<IList>(new int[] { , , }, arrayActual); arrayActual = new List<int>();
breakAfterFunc = p => p > ;
actionTest(array, action, breakBeforeFunc, breakAfterFunc);
AssertCommon.AssertEqual<IList>(new int[] { }, arrayActual);
} [TestMethod()]
public void ForeachHandleTest()
{
ForeachHandleTestHelper(new Action<IEnumerable, Action<int>>(UtilityCommon.ForeachHandle<int>));
} [TestMethod()]
public void ForeachHandleGenericTest()
{
ForeachHandleTestHelper(new Action<IEnumerable<int>, Action<int>>(UtilityCommon.ForeachHandle<int>));
} [TestMethod()]
public void ForeachHandleHasBreakTest()
{
ForeachHandleTestHelper(new Action<IEnumerable, Action<int>, Func<int, bool>, Func<int, bool>>(
UtilityCommon.ForeachHandle<int>), true, true);
} [TestMethod()]
public void ForeachHandleGenericHasBreakTest()
{
ForeachHandleTestHelper(new Action<IEnumerable<int>, Action<int>, Func<int, bool>, Func<int, bool>>(
UtilityCommon.ForeachHandle<int>), true, true);
}
}

测试代码

运行通过测试后执行代码覆盖率分析,结果为100%,如图中选中行所示

【后话】

本文代码在IdleTest的位置如图中选中的文件所示

能力有限文中不免有漏,还望各位前辈同仁后浪海涵并及时指正,不尽感激。我将会坚持写关于单元测试的文章以及更新https://idletest.codeplex.com/,愿与您携手同进步。

VS2012 Unit Test(Void, Action, Func) —— 对无返回值、使用Action或Func作为参数、多重载的方法进行单元测试的更多相关文章

  1. Winform异步解决窗体耗时操作(Action专门用于无返回值,Func专门用于有返回值)

    http://blog.csdn.net/config_man/article/details/25578767 #region 调用timer控件实时查询开关机时间 private void tim ...

  2. 对无返回值、使用Action或Func作为参数、多重载的方法进行单元测试

    VS2012 Unit Test(Void, Action, Func) —— 对无返回值.使用Action或Func作为参数.多重载的方法进行单元测试 [提示] 1. 阅读文本前希望您具备如下知识: ...

  3. 慕课网-Java入门第一季-7-2 Java 中无参无返回值方法的使用

    来源:http://www.imooc.com/code/1578 如果方法不包含参数,且没有返回值,我们称为无参无返回值的方法. 方法的使用分两步: 第一步,定义方法 例如:下面代码定义了一个方法名 ...

  4. Android——关于Activity跳转的返回(无返回值和有返回值)——有返回值

    说明: 跳转页面,并将第一页的Edittext输入的数据通过按钮Button传到第二页用Edittext显示,点击第二页的 返回按钮Button返回第一页(改变第二页的Edittext的内容会传至第一 ...

  5. Java 中无参无返回值方法的使用

    如果方法不包含参数,且没有返回值,我们称为无参无返回值的方法. 方法的使用分两步: 第一步,定义方法 例如:下面代码定义了一个方法名为 show ,没有参数,且没有返回值的方法,执行的操作为输出 “ ...

  6. 面试题----入参两个Integer,无返回值,然后使这个两个值在调用函数后交换

    我最近看到过一个比较好玩的面试题. 写个方法,入参两个Integer,无返回值,然后使这个两个值在调用函数后交换 很有意思的一个题目,引发我的深思,根据一路的学习过来,下面把实现代码贴出来,方便学习. ...

  7. 无返回值的异步方法能否不用await

    1.无返回值的异步方法能否不用await? 如果你不需要等待加一的操作完成,那就可以直接执行后面的操作.那要看你的需求了,如果你后面的操作必须在加一的操作后执行,那就要await了 2.请问C#中如何 ...

  8. ForkJoin有参无返回值、有参有返回值实例

    介绍: a . Fork/Join为JKD1.7引入,适用于对大量数据进行拆分成多个小任务进行计算的框架,最后把所有小任务的结果汇总合并得到最终的结果 b . 相关类 public abstract ...

  9. Java 中无返回值的方法在使用时应该注意的问题

    Java 中的方法是形态多样的.无返回值的方法在使用时应该规避哪些问题呢? 一.不可以打印调用或是赋值调用,只能是单独调用(非常重要): 二.返回值没有,不代表参数就没有: 三.不能return一个具 ...

随机推荐

  1. Jser 设计模式系列之面向对象 - 接口封装与继承

    GOF在<设计模式>中说到:面向接口编程,而非面向实现编程 鉴于此,这个概念可见一斑! JS却不像其他面向对象的高级语言(C#,Java,C++等)拥有内建的接口机制,以确定一组对象和另一 ...

  2. vue小总结

    以下是我在使用vue过程中自己对vue的一些小总结,希望对学习vue的亲们能有所帮助: 1.   http的post请求: this.$http({url: '/someUrl', method: ' ...

  3. iOS开发之画图板(贝塞尔曲线)

    贝塞尔曲线,听着挺牛气一词,不过下面我们在做画图板的时候就用到贝塞尔绘直线,没用到绘制曲线的功能.如果会点PS的小伙伴会对贝塞尔曲线有更直观的理解.这篇博文的重点不在于如何用使用贝塞尔曲线,而是利用贝 ...

  4. EntityFramework 7 Linq Contains In 奇怪问题

    这篇博文纪录一下:当使用 EF7,Linq 实现类似 where filename in('','','') SQL 代码,使用 Contains 出现报错问题. project.json 配置文件( ...

  5. 移动端 h5调试技巧

    一 安卓 一 chrome 1.安卓手机安装chrome浏览器,手机打开开发者模式,用usb线链接电脑,并且允许调试. 2.电脑chrome地址栏输入 chrome://inspect 进入后点击 i ...

  6. jQuery打造智能提示插件二(可编辑下拉框)

    在上一篇 jQuery打造智能提示插件 上改进,增加下拉按钮,修复点击下拉区域外不隐藏BUG 效果 下拉按钮素材: js封装,注意红色部分为BUG修复,然后传入boxwidth不带px: /* /// ...

  7. 编写高质量代码:改善Java程序的151个建议(第1章:JAVA开发中通用的方法和准则___建议16~20)

    建议16:易变业务使用脚本语言编写 Java世界一直在遭受着异种语言的入侵,比如PHP,Ruby,Groovy.Javascript等,这些入侵者都有一个共同特征:全是同一类语言-----脚本语言,它 ...

  8. JS的解析与执行过程

    JS的解析与执行过程 全局中的解析和执行过程 预处理:创建一个词法环境(LexicalEnvironment,在后面简写为LE),扫描JS中的用声明的方式声明的函数,用var定义的变量并将它们加到预处 ...

  9. STM32CubeMX安装指南

    1.STM32CubeMX软件下载 地址:http://pan.baidu.com/s/1bn8sXOV 密码:6u3p 2.安装     1)安装Java SDK     2)安装SetupSTM3 ...

  10. 图片全部加载完成之后再显示页面ui,公司项目里用上,自己写的几行代码

    说明: -----onload事件   这里我并没有考虑ie的兼容性 因为项目是移动端的: -----求大神指正~ -----自己测试正常 页面没加载完之前会有一个提示 /************** ...