C# 单元测试-不可测试代码转换为可测试代码
我读过很多地方,如果你的代码不能测试,那就意味着代码写得不好。因此,我开始编写一个可测试的代码,并开始使用一些单元测试框架 尽管如此,我还是开始寻找一些不可测试的代码并逐渐转换为可测试代码的示例。我找到了大量关于单元测试的例子,但是如果有人能提供一个像上面这样的例子,它可能会为我带来一些新的东西C# 单元测试-不可测试代码转换为可测试代码,c#,unit-testing,C#,Unit Testing,我读过很多地方,如果你的代码不能测试,那就意味着代码写得不好。因此,我开始编写一个可测试的代码,并开始使用一些单元测试框架 尽管如此,我还是开始寻找一些不可测试的代码并逐渐转换为可测试代码的示例。我找到了大量关于单元测试的例子,但是如果有人能提供一个像上面这样的例子,它可能会为我带来一些新的东西 TIA以下是两本帮助您入门的好书: 祝你好运。我并不完全同意。例如,假设您有一个基于计时器执行事情的程序。如果您想以确定性的方式测试它,您必须更改并暂停系统时钟。有大量关于定时事物确定性测试的文献。
TIA以下是两本帮助您入门的好书:
祝你好运。我并不完全同意。例如,假设您有一个基于计时器执行事情的程序。如果您想以确定性的方式测试它,您必须更改并暂停系统时钟。有大量关于定时事物确定性测试的文献。所以一切都是可以测试的,问题是,有多容易。这并不取决于代码编写得有多好,而是取决于程序执行的实际任务以及它是如何设计的,而不是如何实现的。即使有好的设计,您也可以得到难以测试的代码。放一堆代码,并尝试对其进行单元测试。这并不是不可能的,但要想完成这项工作,可能需要进行一些复制粘贴欺骗
protected void buttonClick(object sender, EventArgs e)
{
string currUser =
User.Identity.Name.ToString().Trim()
.Substring(User.Identity.Name.ToString().Trim()
.IndexOf("\\") + 1);
Inventory.Employee.DB objEmpDB = new Inventory.Employee.DB();
Inventory.Employee.Details objEmpDetails =
new Inventory.Employee.Details();
objEmpDetails = objEmpDB.Get(currUser);
Welcome.Text =
"Current User: " + objEmpDetails.Employee_Full_Name;
var objUserDetails = new Inventory.User.Details();
Inventory.User.DB objUserDB = new Inventory.User.DB();
if (objUserDB.UserAuthenticates(currUser))
{
objUserDetails = objUserDB.Get(currUser);
currUserToken = objUserDetails.User_Token.Value;
userID.Text = currUser;
if (objUserDetails.Active_User_Name != objUserDetails.User_Name)
{
lShadow.Text = "Showin: " + objUserDetails.Active_User_Name;
lServer.Text = "(" +
objUserDB.UserPermissionName(objUserDetails.Active_Logon_Name)
+ ") - " + System.Environment.MachineName;
lShadow.ToolTip = Inventory.Properties.Settings.Default
.connectionString.Substring(0, Inventory.Properties
.Settings.Default.connectionString.IndexOf(';'));
divShadow.Visible = true;
}
else
divShadow.Visible = false;
lWelcome.Text = "Current User: " + objUserDetails.User_Name;
}
}
这不仅是因为很难模拟用户的按钮点击,而且还要看看在按钮点击中发生了多少事情。如果你的单元测试失败了,大约有100件怪事可能会出错。枯燥、单一关注点和其他设计原则导致代码易于测试和修复。毕竟,如果您测试的是旅而不是单位,那么单元测试有什么好处:)
更新:(如何修复上述代码)我不会假装上面的代码很容易修复。这是我在过去工作过的代码库中的一个“小”示例。我想展示现实生活中事情会变得多么糟糕 代码有两个主要问题
protected void buttonClick(object sender, EventArgs e)
{
EasyToCallMethod();
}
public void EasyToCallMethod()
{
string currUser =
User.Identity.Name.ToString().Trim()
.Substring(User.Identity.Name.ToString().Trim().IndexOf("\\") + 1);
//...rest of code
}
现在很容易从单元测试调用。但是,这有点傻,因为它确实不能解决第二个问题
轻松修复因此,我们可以利用这个方法调用进行15-20个测试。只需对每一行有特定用途的代码进行测试(比如方法调用的位置),就应该有好的单元测试,这些测试足够小,可以告诉您哪里出了问题,并且代码覆盖率也很好。
高级材料
可以做更多的工作。我们可以实现n层MVC或MVVM。在某个时候,你必须问问自己,你是否过度工程化了。单元测试应该使您的代码更易于维护,但不要将自己过度抽象为虚无。这就是你自己的风格和经验发挥作用的地方。当你觉得自己已经掌握了基础知识时,你应该带着新的问题或是读一本好书来重新学习。使代码更易于(单元)测试的最重要的关键因素是依赖注入 马克·希曼书的第一章 NET中的依赖注入
以PDF文件形式在此处免费提供。它包含了一个非常完整的示例,如何通过减少较低层的依赖关系使紧密耦合的代码更易于测试。您希望使用什么单元测试框架?如果您还没有决定,我建议您使用NUnit。我不同意不可单元测试的代码编写得不好。OO理论101说,在开始实现设计模式之前,应该让代码“做它应该做的”。这会阻止一个人实现下一个变更请求根本无法使用的模式。通常,像敏捷这样的范例会产生“需求完整”的代码,导致客户看到它做了他们想要的事情,但是开发人员没有时间重构,因为客户看到的只是一个成品。这是一个管理/流程问题,不是“写得很差”,而是不完整。@P.Brian.Mackey:每一段应该做它应该做的事情的代码都必须经过测试。这与应用不必要的设计模式无关。使代码单元可测试意味着您甚至无法测试代码,您可以自动重复测试。当你要重构你的代码时,这是一个真正的优势。@Doc我想你误解了我的意思。我不相信你能将OO和dp从单元可测试性中分离出来。他们是一体的。不应用原则/编写“坏代码”/不易测试的代码是类似的概念。这也是促使人们普遍相信“坏代码”可以等同于不可单元测试的代码的原因之一。重构应该完全是开发过程的一部分,并包括这些概念,这些概念使代码单元从一开始就可测试,并在开发过程结束时应用。编写可进行单元测试以启动的代码会导致浪费抽象。@DOC您可以使用CRUD或一些公共接口来帮助使用TDD加快开发过程,但这仍然需要规划OO,可能还需要规划dp。它仍然不能使一个人摆脱臃肿的方法、枯燥的方法,或者防止其他原则被滥用。+1用于有效地处理遗留代码。充满了伟大的例子。任何一个萌芽的TDDer都必须阅读。这里有几个优点,但我相信OP正在寻找一个简单的例子,使单元测试即使不是完全没有意义,也会有一些意义。我怀疑他在试图掌握单元测试/TDD的基础知识的同时,是否还在寻找测试复杂性理论。谢谢,您将如何编写相同的代码使其可测试?