简体   繁体   English

如何在 c# 中为以下代码编写 nunit 测试用例? 如何为没有 arguments 的方法编写 nunit 测试用例?

[英]How to write nunit test case for below code in c#? How ro write nunit test cases for methods with no arguments?

How do I write unit test (using nUnit) a class that has methods with no arguments in c#?如何编写单元测试(使用 nUnit)一个 class,它的方法在 c# 中没有 arguments? I am calling this showinfo() method in Program.cs.我在 Program.cs 中调用这个 showinfo() 方法。 I want to write an nunit test case for that...but since it contains no arguments how can i test it?我想为此编写一个 nunit 测试用例......但由于它不包含 arguments 我该如何测试它?

 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();
                }
            }

There main issue here that's making your code difficult to test is that this one method is doing too much.这里使您的代码难以测试的主要问题是这种方法做得太多了。

What do you want to test?你想测试什么? In it's simplest you'll want to test the basic happy path where the user enters the correct password (more on that in a minute) and that the correct account details are output.在最简单的情况下,您需要测试用户输入正确密码的基本快乐路径(稍后会详细介绍),并且正确的帐户详细信息是 output。 You'll also want to test the 'unhappy path' where the user enters an incorrect password, and is shown an error message instead of the user's account details.您还需要测试用户输入错误密码并显示错误消息而不是用户帐户详细信息的“不愉快路径”。

Further complicating this is that, in the context of your code, Console.WriteLine is a 3rd party dependency, albeit from a reputable source.更复杂的是,在您的代码上下文中, Console.WriteLine是第 3 方依赖项,尽管它来自信誉良好的来源。 In a unit test our aim is to isolate a 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.这意味着您不想在您正在单元测试的方法中调用真正的Console.WriteLine However, because Console.WriteLine is a static method it's difficult to mock , which is the approach you would normally take to isolate a unit of work.但是,因为Console.WriteLine是一个 static 方法,所以很难模拟,这是您通常用来隔离工作单元的方法。 Of course you still want to ensure that the details that will be written to the screen are as expected.当然,您仍然希望确保将写入屏幕的详细信息符合预期。 There are multiple approaches to this but the easiest is probably to separate the code which constructs the user output from the code which actually displays it .有多种方法可以解决这个问题,但最简单的方法可能是将构建用户 output 的代码与实际显示它的代码分开

Another issue here is that this method relies on state in the Program class (the myId , myPass , myName etc fields).这里的另一个问题是,此方法依赖于Program class 中的 state( myIdmyPassmyName等字段)。 In a unit test you need to control the values that you pass into these variables, and you can't do this if they're static members in your Program class.在单元测试中,您需要控制传递给这些变量的值,如果它们是Program class 中的 static 成员,则无法执行此操作。 As a starter you could pass these into your showInfo() method, allowing you to control what you pass in to a unit test.作为初学者,您可以将这些传递到您的showInfo()方法中,从而允许您控制传递给单元测试的内容。

Summary: to make this code unit testable you need to break it down into smaller components that you can unit test individually.摘要:要使此代码可单元测试,您需要将其分解为可以单独进行单元测试的较小组件。

Further note: your .Contains test for the user ID and password looks suspect, what happens if two people have the same password?进一步说明:您的.Contains用户 ID 和密码测试看起来很可疑,如果两个人有相同的密码会发生什么?

Edit Here's an example of how you might set about breaking this method down into smaller methods that might be individually unit tested.编辑这是一个示例,说明您如何着手将此方法分解为可以单独进行单元测试的较小方法。 Note that there are still improvements that could be made to this code, but I've intentionally left this quite similar to the code in the OP so it's easier to draw comparisons between the two:请注意,仍然可以对此代码进行改进,但我故意将其保留为与 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;
    }
}

You would then write unit tests for the IsPasswordValid and GetSuccessOutput methods.然后,您将为IsPasswordValidGetSuccessOutput方法编写单元测试。

There are many ways to test methods with no arguments, private methods, etc. You just need to set something up, so when you do test your method, it calls another method (with arguments)有很多方法可以测试没有 arguments、私有方法等的方法。你只需要设置一些东西,所以当你测试你的方法时,它会调用另一个方法(带参数)

In your AssemblyInfo.cs , you add an attribute在您的AssemblyInfo.cs中,您添加一个属性

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

The do something like this in your class you want to test在你想要测试的 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
    }
}

As you see, this is pretty convoluted.如您所见,这非常令人费解。 There are other variations of this.这还有其他变化。 Most important - you might noted that you need to compile and run unit tests in DEBUG mode for some code to be available for unit testing but not in release assemblies.最重要的是 - 您可能注意到您需要在调试模式下编译和运行单元测试,以便某些代码可用于单元测试,但不能用于发布程序集。 Use conditional compilation for this.为此使用条件编译。

But the truth is, this is bad design!但事实是,这是糟糕的设计! You should design your classes in such way so that you can truly unit test important functionality and you can do functional tests.您应该以这样的方式设计您的类,以便您可以真正对重要功能进行单元测试,并且可以进行功能测试。 You can use mocks, etc. Design ahead.您可以使用模拟等。提前设计。 What I just showed - we use to test some old code, where people wrote it - UI logic, BLL logic and Data Access logic all in same file.我刚刚展示的内容——我们用来测试人们编写的一些旧代码——UI 逻辑、BLL 逻辑和数据访问逻辑都在同一个文件中。 There is no way, you should write a new code like this没办法,你应该像这样写一个新代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM