【问题标题】:How to write nunit test case for below code in c#? How ro write nunit test cases for methods with no arguments?如何在 c# 中为以下代码编写 nunit 测试用例?如何为没有参数的方法编写 nunit 测试用例?
【发布时间】:2020-04-14 17:17:00
【问题描述】:

如何在 c# 中编写单元测试(使用 nUnit)一个具有不带参数的方法的类? 我在 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();
                }
            }

【问题讨论】:

    标签: c# unit-testing testing nunit


    【解决方案1】:

    这里使您的代码难以测试的主要问题是这种方法做得太多。

    你想测试什么?在最简单的情况下,您需要测试基本的happy path,用户输入正确的密码(稍后会详细介绍)并输出正确的帐户详细信息。您还需要测试用户输入错误密码并显示错误消息而不是用户帐户详细信息的“不愉快路径”。

    更复杂的是,在您的代码上下文中,Console.WriteLine 是第 3 方依赖项,尽管来自信誉良好的来源。在单元测试中,我们的目标是隔离unit of work。这意味着您不想在单元测试的方法中调用真正的Console.WriteLine。但是,因为Console.WriteLine 是一种静态方法,所以很难mock,这是您通常用来隔离工作单元的方法。当然,您仍然希望确保将写入屏幕的详细信息符合预期。有多种方法可以解决此问题,但最简单的方法可能是将构建用户输出的代码与实际显示它的代码分开

    这里的另一个问题是此方法依赖于 Program 类中的状态(myIdmyPassmyName 等字段)。在单元测试中,您需要控制传递给这些变量的值,如果它们是 Program 类中的静态成员,则不能这样做。作为初学者,您可以将这些传递到您的 showInfo() 方法中,从而允许您控制传递给单元测试的内容。

    总结:要使此代码可单元测试,您需要将其分解为可以单独进行单元测试的更小的组件。

    进一步说明:您对用户 ID 和密码的 .Contains 测试看起来很可疑,如果两个人的密码相同会怎样?

    编辑 这是一个示例,说明您如何着手将此方法分解为可以单独进行单元测试的较小方法。请注意,仍然可以对此代码进行改进,但我故意将其保留为与 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;
        }
    }
    

    然后您将为IsPasswordValidGetSuccessOutput 方法编写单元测试。

    【讨论】:

      【解决方案2】:

      有很多方法可以测试不带参数的方法、私有方法等。你只需要设置一些东西,所以当你测试你的方法时,它会调用另一个方法(带参数)

      在您的AssemblyInfo.cs 中,添加一个属性

      #if DEBUG #then
      InternalsVisibleTo("your test project")`.
      #endif
      

      在你想测试的课堂上做这样的事情

      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
          }
      }
      
      

      如您所见,这非常令人费解。这还有其他变化。最重要的是 - 您可能注意到您需要在调试模式下编译和运行单元测试,以便某些代码可用于单元测试,但不能用于发布程序集。为此使用条件编译。

      但事实是,这是糟糕的设计!您应该以这样的方式设计您的类,以便您可以真正对重要功能进行单元测试,并且可以进行功能测试。您可以使用模拟等。提前设计。我刚刚展示的内容——我们用来测试人们编写的一些旧代码——UI 逻辑、BLL 逻辑和数据访问逻辑都在同一个文件中。没办法,你应该这样写一段新代码

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-25
        • 1970-01-01
        • 2013-07-07
        • 2023-01-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-08
        相关资源
        最近更新 更多