如何在 C# 中为以下代码编写 nunit 测试用例?如何为没有参数的方法编写 nunit 测试用例?
How to write nunit test case for below code in c#? How ro write nunit test cases for methods with no arguments?
我如何编写单元测试(使用 nUnit)一个 class 在 c# 中有没有参数的方法?
我在 Program.cs 中调用这个 showinfo() 方法。我想为此编写一个 nunit 测试用例...但是由于它不包含任何参数,我该如何测试它?
public void showInfo() //no arguments are passes here and im calling this in Program.cs
{
int indexNum;
string inId = Convert.ToString(Console.ReadLine());//taking Id as input
Console.Write("Enter account Password :");
string inPass = Convert.ToString(Console.ReadLine());//taking password
if (myId.Contains(inId)&&myPass.Contains(inPass))
{
Console.ForegroundColor= ConsoleColor.Green;
Console.WriteLine("Login Success!", Console.ForegroundColor);
Console.ResetColor();
indexNum = Array.IndexOf(myId, inId);
Console.WriteLine("Your details: ");
Console.WriteLine("Name: " + myName[indexNum]);
Console.WriteLine("Id: " + myId[indexNum]);
Console.WriteLine("Acc Type: " + myAccType[indexNum]);
Console.WriteLine("Date of Joining: " + myDob[indexNum]);
Console.WriteLine("Domain: " + myDomain[indexNum]);
Console.WriteLine("Manager: " + myManager[indexNum]);
//Console.WriteLine("Employee name: " + empl_name[indexNum]);
//Console.WriteLine(myId);
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Login Error!",Console.ForegroundColor);
Console.ResetColor();
}
}
有很多方法可以测试不带参数的方法、私有方法等。您只需要设置一些东西,这样当您测试您的方法时,它会调用另一个方法(带参数)
在您的 AssemblyInfo.cs
中,您添加了一个属性
#if DEBUG #then
InternalsVisibleTo("your test project")`.
#endif
在你想要测试的class中做这样的事情
public class A
{
#if DEBUG #then
private MethodParameters _testParameters
internal void SetMethodParameters(MethodParameters testParameters)
{
_testParameters = testParameters
}
#endif
public void NoArgumentMethod()
{
#if DEBUG #then
if (_testParameters)
{
PrivateArgumentMethod(_testParameters);
return;
}
#endif
// collect your arguments here and pass to private method
var mp = new MethodParameters();
mp.Id = _someId;
// . . . . .
}
private void PrivateArgumentMethod(MethodParameters testParameters)
{
// execute here
}
internal class MethodParameters
{
public int Id {get; set;}
// more properties here
}
}
如您所见,这非常令人费解。这还有其他变体。最重要的是 - 您可能注意到您需要在 DEBUG 模式下编译和 运行 单元测试,以便某些代码可用于单元测试但不能用于发布程序集。为此使用条件编译。
但事实是,这是糟糕的设计!您应该以这样的方式设计您的 classes,以便您可以真正对重要功能进行单元测试,并且您可以做功能测试。您可以使用模拟等。提前设计。我刚才展示的 - 我们用来测试一些旧代码,人们在其中编写代码 - UI 逻辑、BLL 逻辑和数据访问逻辑都在同一个文件中。没办法,你应该这样写一个新的代码
使您的代码难以测试的主要问题是这种方法做的太多了。
你想测试什么?在最简单的情况下,您需要测试基本的 happy path,用户在其中输入正确的密码(稍后会详细介绍)并输出正确的帐户详细信息。您还需要测试 'unhappy path' 用户输入不正确的密码,并显示一条错误消息而不是用户的帐户详细信息。
更复杂的是,在您的代码上下文中,Console.WriteLine
是第 3 方依赖项,尽管来自信誉良好的来源。在单元测试中,我们的目标是隔离 unit of work. What this means is that you don't want to be calling the real Console.WriteLine
in the method that you're unit testing. However, because Console.WriteLine
is a static method it's difficult to mock,这是隔离工作单元通常采用的方法。当然,您仍然希望确保将写入屏幕的详细信息符合预期。有多种方法,但最简单的可能是 将构造用户输出的代码与实际显示它的代码分开 。
这里的另一个问题是此方法依赖于 Program
class(myId
、myPass
、myName
等字段中的状态)。在单元测试中,您需要控制传递给这些变量的值,如果它们是 Program
class 中的静态成员,则无法执行此操作。作为初学者,您可以将这些传递到您的 showInfo()
方法中,从而允许您控制传递给单元测试的内容。
总结:要使此代码可单元测试,您需要将其分解为更小的组件,您可以单独对其进行单元测试。
进一步注意:您的 .Contains
用户 ID 和密码测试看起来很可疑,如果两个人使用相同的密码会怎样?
编辑
下面是一个示例,说明您可以如何着手将此方法分解为更小的方法,这些方法可以单独进行单元测试。请注意,此代码仍然可以进行改进,但我故意将其保留为与 OP 中的代码非常相似,因此更容易在两者之间进行比较:
public class Class1
{
public void showInfo(
string[] myId,
string[] myPass,
string[] myName,
string[] myAccType,
string[] myDob,
string[] myDomain,
string[] myManager)
{
int indexNum;
string inId = Convert.ToString(Console.ReadLine());//taking Id as input
Console.Write("Enter account Password :");
string inPass = Convert.ToString(Console.ReadLine());//taking password
if (IsPasswordValid(inId, inPass, myId, myPass))
{
var successOutputLines = GetSuccessOutput(
inId,
myId,
myPass,
myName,
myAccType,
myDob,
myDomain,
myManager);
successOutputLines.ToList().ForEach(l => Console.WriteLine(l));
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Login Error!", Console.ForegroundColor);
Console.ResetColor();
}
}
public bool IsPasswordValid(
string userId,
string passwordIn,
string[] userIds,
string[] passwords)
{
var userIndex = Array.IndexOf(userIds, userId);
if (userIndex == -1)
throw new ArgumentException(nameof(userId));
var expectedPassword = passwords[userIndex];
return expectedPassword == passwordIn;
}
public IEnumerable<string> GetSuccessOutput(
string userId,
string[] myId,
string[] myPass,
string[] myName,
string[] myAccType,
string[] myDob,
string[] myDomain,
string[] myManager)
{
var userIndex = Array.IndexOf(myId, userId);
if (userIndex == -1)
throw new ArgumentException(nameof(userId));
var lines = new List<string>();
lines.Add("Login Success!");
lines.Add("Your details: ");
lines.Add("Your details: ");
lines.Add("Name: " + myName[userIndex]);
lines.Add("Id: " + myId[userIndex]);
lines.Add("Acc Type: " + myAccType[userIndex]);
lines.Add("Date of Joining: " + myDob[userIndex]);
lines.Add("Domain: " + myDomain[userIndex]);
lines.Add("Manager: " + myManager[userIndex]);
return lines;
}
}
然后您将为 IsPasswordValid
和 GetSuccessOutput
方法编写单元测试。
我如何编写单元测试(使用 nUnit)一个 class 在 c# 中有没有参数的方法? 我在 Program.cs 中调用这个 showinfo() 方法。我想为此编写一个 nunit 测试用例...但是由于它不包含任何参数,我该如何测试它?
public void showInfo() //no arguments are passes here and im calling this in Program.cs
{
int indexNum;
string inId = Convert.ToString(Console.ReadLine());//taking Id as input
Console.Write("Enter account Password :");
string inPass = Convert.ToString(Console.ReadLine());//taking password
if (myId.Contains(inId)&&myPass.Contains(inPass))
{
Console.ForegroundColor= ConsoleColor.Green;
Console.WriteLine("Login Success!", Console.ForegroundColor);
Console.ResetColor();
indexNum = Array.IndexOf(myId, inId);
Console.WriteLine("Your details: ");
Console.WriteLine("Name: " + myName[indexNum]);
Console.WriteLine("Id: " + myId[indexNum]);
Console.WriteLine("Acc Type: " + myAccType[indexNum]);
Console.WriteLine("Date of Joining: " + myDob[indexNum]);
Console.WriteLine("Domain: " + myDomain[indexNum]);
Console.WriteLine("Manager: " + myManager[indexNum]);
//Console.WriteLine("Employee name: " + empl_name[indexNum]);
//Console.WriteLine(myId);
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Login Error!",Console.ForegroundColor);
Console.ResetColor();
}
}
有很多方法可以测试不带参数的方法、私有方法等。您只需要设置一些东西,这样当您测试您的方法时,它会调用另一个方法(带参数)
在您的 AssemblyInfo.cs
中,您添加了一个属性
#if DEBUG #then
InternalsVisibleTo("your test project")`.
#endif
在你想要测试的class中做这样的事情
public class A
{
#if DEBUG #then
private MethodParameters _testParameters
internal void SetMethodParameters(MethodParameters testParameters)
{
_testParameters = testParameters
}
#endif
public void NoArgumentMethod()
{
#if DEBUG #then
if (_testParameters)
{
PrivateArgumentMethod(_testParameters);
return;
}
#endif
// collect your arguments here and pass to private method
var mp = new MethodParameters();
mp.Id = _someId;
// . . . . .
}
private void PrivateArgumentMethod(MethodParameters testParameters)
{
// execute here
}
internal class MethodParameters
{
public int Id {get; set;}
// more properties here
}
}
如您所见,这非常令人费解。这还有其他变体。最重要的是 - 您可能注意到您需要在 DEBUG 模式下编译和 运行 单元测试,以便某些代码可用于单元测试但不能用于发布程序集。为此使用条件编译。
但事实是,这是糟糕的设计!您应该以这样的方式设计您的 classes,以便您可以真正对重要功能进行单元测试,并且您可以做功能测试。您可以使用模拟等。提前设计。我刚才展示的 - 我们用来测试一些旧代码,人们在其中编写代码 - UI 逻辑、BLL 逻辑和数据访问逻辑都在同一个文件中。没办法,你应该这样写一个新的代码
使您的代码难以测试的主要问题是这种方法做的太多了。
你想测试什么?在最简单的情况下,您需要测试基本的 happy path,用户在其中输入正确的密码(稍后会详细介绍)并输出正确的帐户详细信息。您还需要测试 'unhappy path' 用户输入不正确的密码,并显示一条错误消息而不是用户的帐户详细信息。
更复杂的是,在您的代码上下文中,Console.WriteLine
是第 3 方依赖项,尽管来自信誉良好的来源。在单元测试中,我们的目标是隔离 unit of work. What this means is that you don't want to be calling the real Console.WriteLine
in the method that you're unit testing. However, because Console.WriteLine
is a static method it's difficult to mock,这是隔离工作单元通常采用的方法。当然,您仍然希望确保将写入屏幕的详细信息符合预期。有多种方法,但最简单的可能是 将构造用户输出的代码与实际显示它的代码分开 。
这里的另一个问题是此方法依赖于 Program
class(myId
、myPass
、myName
等字段中的状态)。在单元测试中,您需要控制传递给这些变量的值,如果它们是 Program
class 中的静态成员,则无法执行此操作。作为初学者,您可以将这些传递到您的 showInfo()
方法中,从而允许您控制传递给单元测试的内容。
总结:要使此代码可单元测试,您需要将其分解为更小的组件,您可以单独对其进行单元测试。
进一步注意:您的 .Contains
用户 ID 和密码测试看起来很可疑,如果两个人使用相同的密码会怎样?
编辑 下面是一个示例,说明您可以如何着手将此方法分解为更小的方法,这些方法可以单独进行单元测试。请注意,此代码仍然可以进行改进,但我故意将其保留为与 OP 中的代码非常相似,因此更容易在两者之间进行比较:
public class Class1
{
public void showInfo(
string[] myId,
string[] myPass,
string[] myName,
string[] myAccType,
string[] myDob,
string[] myDomain,
string[] myManager)
{
int indexNum;
string inId = Convert.ToString(Console.ReadLine());//taking Id as input
Console.Write("Enter account Password :");
string inPass = Convert.ToString(Console.ReadLine());//taking password
if (IsPasswordValid(inId, inPass, myId, myPass))
{
var successOutputLines = GetSuccessOutput(
inId,
myId,
myPass,
myName,
myAccType,
myDob,
myDomain,
myManager);
successOutputLines.ToList().ForEach(l => Console.WriteLine(l));
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Login Error!", Console.ForegroundColor);
Console.ResetColor();
}
}
public bool IsPasswordValid(
string userId,
string passwordIn,
string[] userIds,
string[] passwords)
{
var userIndex = Array.IndexOf(userIds, userId);
if (userIndex == -1)
throw new ArgumentException(nameof(userId));
var expectedPassword = passwords[userIndex];
return expectedPassword == passwordIn;
}
public IEnumerable<string> GetSuccessOutput(
string userId,
string[] myId,
string[] myPass,
string[] myName,
string[] myAccType,
string[] myDob,
string[] myDomain,
string[] myManager)
{
var userIndex = Array.IndexOf(myId, userId);
if (userIndex == -1)
throw new ArgumentException(nameof(userId));
var lines = new List<string>();
lines.Add("Login Success!");
lines.Add("Your details: ");
lines.Add("Your details: ");
lines.Add("Name: " + myName[userIndex]);
lines.Add("Id: " + myId[userIndex]);
lines.Add("Acc Type: " + myAccType[userIndex]);
lines.Add("Date of Joining: " + myDob[userIndex]);
lines.Add("Domain: " + myDomain[userIndex]);
lines.Add("Manager: " + myManager[userIndex]);
return lines;
}
}
然后您将为 IsPasswordValid
和 GetSuccessOutput
方法编写单元测试。