配合NUnit时行单元测试的基类,可以在控制台显示表格
上一篇文章(在这里),总结了和NUnit单元测试工具,其中提到可以把测试类中把信息在控制台输出。不过在NUnit中,只是直接输出文本,想要输出复杂的内容,那就得自己实现了。
我的职业和任务主要就是Web 应用程序,里面有无数次是和数据库打交道的,数据库的话当然就少不了DataTable,如果能在测试的过程中,显示出来DataTable里面的数据,当然再好不过了。
有了这个想法,那就行动吧。
图一
这里面我封装一个基类:class TestBase,它不加[TestFixture]指令,只是准备给其它使用NUnit进行单元测试的类做继承。具体的功能就是封装了两个方法。
- SayTable(DataTable dt) 在控制台显示表格内的数据,“图一”就是它运行起来的结果
- Say(string str) 包装了 Console.WriteLine(str) ,这样有了一个比较简洁的方法名称
- 这个类还有一个属性:public int Length4CutStr 这个属性是控制cell (单元格)内截取文字长度的。默认我设定为25
下面重点介绍一下SayTable这个方法。
这个方法的设计目标是:(一)可以显示表格里面的数据;(二)可以包含字段(列)的名称;(三)自动适应字段(列)内最长的那个cell;(四)使用制表符给表格内容加上边框
前两个目标比较容易实现,只要循环Columns或者Rows就可以完成了。
关键是后面这两个,经过反复研究,也没有找到最完美的办法,也就是始终于法将内容对齐。最开始的时候以为,使用两个空格就能补充一个汉字的位置,用肉眼观察似乎是这样,结果发现,不是那么回事, 最后截图在再放大发现,在NUnit的控制台界面上,英文是7个像素,而中文是13像素。按照中文英文1:2的方式,100个中文和200个英文或者数字,分两行左端对齐,就能看到,英文那一行多出来14个字。换了一种算法,不简单的按照个数,先计算出来空隙,然后再计算字符的数量,这个时候,又出现了小于7像素,不能用英文空格补齐的问题。不过总体来看,大体上还是对齐了,至少对于测试来说,不会让人混淆表格里面的数据。虽然有点勉强,但是基本的目标也已经实现了。
下面给出类的代码:
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | /// <summary> /// 配合NUnit时行单元测试的基类 /// 主要的作用是在控制台输出文本或者DataTable里面的内容 /// 作者:xpnew.cnblogs.com http://blog.csdn.net/xpnew /// 发布日期:2010年8月11日 /// 更新日期:2010年8月11日 /// </summary> public class TestBase { public void Say( string str) { Console.WriteLine(str); } private int _Length4CutStr = 25; public int Length4CutStr { get { return _Length4CutStr; } set { _Length4CutStr = value; } } //private ArrayList _ColLength; private List< int > _ColLength = new List< int >(); /// <summary> /// 在控制台显示DataTable的内容,可以自适应Cell的宽度,但是最多不能超过Length4CutStr属性指定的值 /// </summary> /// <param name="dt"></param> public void SayTable(DataTable dt) { TextCount tc = new TextCount(); //获得列的数量 int col_count = dt.Columns.Count; string col_str; //单元格的内容; int field_length = 0; int field_length1 = 0; for ( int i = 0; i < col_count; i++) { tc.str = dt.Columns[i].ColumnName; field_length =(tc.ChLength*13+tc.EnLength*7)/13; for ( int j = 0; j < dt.Rows.Count; j++) { col_str = dt.Rows[j][i].ToString(); if (dt.Columns[i].DataType == typeof ( string ) && col_str.Length > Length4CutStr) { col_str = col_str.Substring(0, Length4CutStr); dt.Rows[j][i] = col_str; } tc.str = col_str; field_length1 = (tc.ChLength * 13 + tc.EnLength * 7) / 13; field_length = field_length > field_length1 ? field_length : field_length1; } field_length ++; _ColLength.Add(field_length); } char [] line_left = new char [3] { '┏' , '┣' , '┗' }; char [] line_h = new char [3] { '━' , '━' , '━' }; char [] line_mid2 = new char [3] { '┳' , '╋' , '┻' }; char [] line_v = new char [3] { '┃' , '┃' , '┃' }; char [] line_right = new char [3] { '┓' , '┫' , '┛' }; StringBuilder sb = new StringBuilder(); Say( "显示表格数据==========================" ); //行首: sb.Append(line_left[0]); for ( int i = 0; i < col_count; i++) { if (i != 0) sb.Append(line_mid2[0]); for ( int j =0 ; j< _ColLength[i]; j++){ sb.Append(line_h[0]); } } sb.Append(line_right[0]); sb.Append( '\n' ); for ( int i = 0; i < col_count; i++) { sb.Append(line_v[0]); sb.Append(FillString(dt.Columns[i].ColumnName.Trim(), _ColLength[i])); } sb.Append(line_v[0]); sb.Append( '\n' ); foreach (DataRow row in dt.Rows) { //行间: sb.Append(line_left[1]); for ( int i = 0; i < col_count; i++) { if (i != 0) sb.Append(line_mid2[1]); for ( int j = 0; j < _ColLength[i]; j++) { sb.Append(line_h[1]); } } sb.Append(line_right[1]); sb.Append( '\n' ); sb.Append(line_v[0]); for ( int i = 0; i < dt.Columns.Count; i++) { sb.Append(FillString(row[i].ToString().Trim(), _ColLength[i])); sb.Append(line_v[0]); } sb.Append( "\n" ); } //行尾: sb.Append(line_left[2]); for ( int i = 0; i < col_count; i++) { if (i != 0) sb.Append(line_mid2[2]); for ( int j = 0; j < _ColLength[i] ; j++) { sb.Append(line_h[2]); } } sb.Append(line_right[2]); sb.Append( '\n' ); Console.Write(sb.ToString()); } private string FillString( string str, int len) { TextCount tc = new TextCount(str); int len1 = tc.ChLength * 13 + tc.EnLength * 7; int len2 = len*13 - len1; if (tc.EnLength > 0) { if (len2 > 7) { //len2 = len2 / 7 + (len2 % 7 > 0 ? 1 : 0); str = str + " " .PadRight(len2 / 7, ' ' ); } int len3 = len2 % 7; if (len3 > 4) { str = str + " " ; } } else { if (len2 > 13) { //len2 = len2 / 7 + (len2 % 7 > 0 ? 1 : 0); str = str + " " .PadRight(len2 / 13, ' ' ); } int len3 = len2 % 13; if (len3 > 7) { str = str + " " ; } } return str; } } |
另外的一个类: TextCount,可以分别统计中英文字符的个数(在当前应用中,这个是主要的作用),也可以统计中文英标点符号的个数。
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | /// <summary> /// 字符统计的功能 /// </summary> public class TextCount { private StringBuilder sb; private string _str; public string str { get { return _str; } set { _str = value; Analyze(); } } public TextCount() { } public TextCount( string s) { _str = s; Analyze(); } private int _len = 0; /// <summary> /// 粗略统计,等同于String.Length /// </summary> public int len { get { return _str.Length; } } private int _Len = 0; /// <summary> /// 精确长度,等同于AllLength /// </summary> public int Len { get { return _Len; } } private int _AllLength = 0; /// <summary> /// 全部长度 /// </summary> public int AllLength { get { return _AllLength; } } private int _ChLength = 0; /// <summary> /// 中文字数 /// </summary> public int ChLength { get { return _ChLength; } } private int _EnLength = 0; /// <summary> /// 英文字数 /// </summary> public int EnLength { get { return _EnLength; } } private int _LetterLength = 0; /// <summary> /// 纯字母字数 /// </summary> public int LetterLength { get { return _LetterLength; } } /// <summary> /// 英文符号数量 /// </summary> private int _SymbolLength = 0; public int SymbolLength { get { return _SymbolLength; } } private int _ChSymbolLength = 0; /// <summary> /// 中文符号字数 /// </summary> /// <remarks>这个只能获取预定义中文符号列表</remarks> public int ChSymbolLength { get { return _ChSymbolLength; } } private string _ChSymbilDefine = ",。;“”:?、!《》·「」『』〖〗【】※¥" ; /// <summary> /// 解析字符串,完成统计,每次发第生改变都需要调用 /// </summary> private void Analyze() { _EnLength = _ChLength = _AllLength = _Len = _LetterLength = _SymbolLength = _ChSymbolLength = 0; for ( int i = 0; i <_str.Length; i++) { //计算文本长度,区分中英文字符,中文算两个长度,英文算一个长度 byte [] byte_len = Encoding.Default.GetBytes(_str.Substring(i, 1)); if (byte_len.Length > 1) { _ChLength++; //如果长度大于1,是中文,占两个字节,+2 if (_ChSymbilDefine.IndexOf(_str.Substring(i, 1)) >-1) { _ChSymbolLength++; } } else { _EnLength++; //如果长度等于1,是英文,占一个字节,+1 /******************************** * 其实可以连数字、空格都给统计出来,但是实际中用处不多 * if (Char.IsLetter(_str[i])) { //_LetterLength++; }else if(Char.IsNumber(_str[i])) { } * ************************************** */ if (Char.IsLetterOrDigit(_str[i])) _LetterLength++; } } _AllLength = _Len = _EnLength + _ChLength*2; _SymbolLength = _EnLength - _LetterLength; } } |
另外,本文参考了《控制台打印表格》 这篇文章是C++的代码,我看不太懂,不过里面使用char[]数组存储制表符的思想,我还是照搬过来了,实践证明,这咱思想非常好,在此深表感谢!
漂泊是无尽的轮回。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了