Can I write into console in a unit test? If yes, w

2019-01-13 05:28发布

问题:

I have a test project in Visual Studio. I use Microsoft.VisualStudio.TestTools.UnitTesting.

I add this line in one of my unit tests:

Console.WriteLine("Some foo was very angry with boo");
Console.ReadLine();

When I run the test, test passes but console window is not opened at all.

Is there a way to make the Console window available to be interacted via a unit test?

回答1:

NOTE: The original answer below should work for any version of VS up through VS2012. VS2013 does not appear to have a Test Results window anymore. Instead, if you need test-specific output you can use @Stretch's suggestion of Trace.Write() to write output to the Output window.


The Console.Write method does not write to the "console" -- it writes to whatever is hooked up to the standard output handle for the running process. Similarly, Console.Read reads input from whatever is hooked up to the standard input.

When you run a unit test through VS2010, standard output is redirected by the test harness and stored as part of the test output. You can see this by right-clicking the Test Results window and adding the column named "Output (StdOut)" to the display. This will show anything that was written to stdout.

You could manually open a console window, using P/Invoke as @sinni800 says. From reading the AllocConsole documentation, it appears that the function will reset stdin and stdout handles to point to the new console window. (I'm not 100% sure about that; it seems kinda wrong to me if I've already redirected stdout for Windows to steal it from me, but I haven't tried.)

In general, though, I think it's a bad idea; if all you want to use the console for is to dump more information about your unit test, the output is there for you. Keep using Console.WriteLine the way you are, and check the output results in the Test Results window when it's done.



回答2:

Someone commented about this apparently new functionality in VS2013. I wasn't sure what he meant at first, but now that I do, I think it deserve's it's own answer.

We can use Console.WriteLine normally and the output is displayed, just not in the Output window, but in a new window after we click "Output" in the test details.



回答3:

You could use this line to write to Output Window of the Visual Studio:

System.Diagnostics.Debug.WriteLine("Matrix has you...");

Hope that helps



回答4:

As stated, unit tests are designed to run without interaction.

However, you can Debug unit tests, just like any other code. The easiest way is to use the Debug button in the Test Results tab.

Being able to Debug means being able to use breakpoints. Being able to use breakpoints, then, means being able to use Tracepoints, which I find extremely useful in every day debugging.

Essentially, Tracepoints allow you to write to the Output window (or, more accurately, to standard output). Optionally, you can continue to run, or you can stop like a regular breakpoint. This gives you the "functionality" you are asking for, without the need to rebuild your code, or fill it up with debug information.

Simply add a breakpoint, and then right-click on that breakpoint. Select the "When Hit..." option:

Which brings up the dialog:

A few things to note:

  1. Notice that the breakpoint is now shown as a diamond, instead of a sphere, indicating a trace point
  2. You can output the value of a variable by enclosing it like {this}.
  3. Uncheck the "Continue Execution" checkbox to have the code break on this line, like any regular breakpoint
  4. You have the option of running a macro. Please be careful - you may cause harmful side effects.

See the documentation for more details.



回答5:

There are several ways to write output from a Visual Studio unit test in C#:

  • Console.Write - The Visual Studio test harness will capture this and show it when you select the test in the Test Explorer and click the Output link. Does not show up in the Visual Studio Output Window when either running or debugging a unit test (arguably this is a bug).
  • Debug.Write - The Visual Studio test harness will capture this and show it in the test output. Does appear in the Visual Studio Output Window when debugging a unit test, unless Visual Studio Debugging options are configured to redirect Output to the Immediate Window. Nothing will appear in the Output (or Immediate) Window if you simply run the test without debugging. By default only available in a Debug build (that is, when DEBUG constant is defined).
  • Trace.Write - The Visual Studio test harness will capture this and show it in the test output. Does appear in the Visual Studio Output (or Immediate) Window when debugging a unit test (but not when simply running the test without debugging). By default available in both Debug and Release builds (that is, when TRACE constant is defined).

Confirmed in Visual Studio 2013 Professional.



回答6:

You can use

Trace.WriteLine() 

to write to the Output window when debugging a unittest.



回答7:

In visual Studio 2017, "TestContext" doesn't show Output link into Test Explorer. However, Trace.Writeline() shows the Ouput link.



回答8:

First of all unit tests are, by design, supposed to run completely without interaction.

With that aside, I don't think there's a possibility that was thought of.

You could try hacking with the AllocConsole P/Invoke which will open a console even when your current application is a GUI application. The Console class will then post to the now opened console.



回答9:

Debug.WriteLine() can be used as well.



回答10:

This exactly not a solution , but an approach from the book the

art of unit testing by Roy Osherove

we need stubs to break these dependencies , like writing to FileSystem or writing to Event Log or Writing to Console -

Stub could be passed into Main Class and if stub not null then write to Stub. However it can change the api (like now constructor has a stub as parameter). The other approach is inheriting and creating a Mock Object. which is described below.

    namespace ClassLibrary1
    {
       // TO BE TESTED
        public class MyBusinessClass
        {
            ConsoleStub myConsoleForTest;
            public MyBusinessClass()
            {
                // Constructor
            }

            // This is test stub approach - 2
            public MyBusinessClass(ConsoleStub console)
            {
                this.myConsoleForTest = console;
            }

            public virtual void MyBusinessMethod(string s)
            {
                // this needs to be unit tested
                Console.WriteLine(s);

                // Just an example - you need to be creative here
                // there are many ways 
                if (myConsoleForTest !=null){
                    myConsoleForTest.WriteLine(s);
                }
            }
        }

        public class ConsoleStub
        {
            private string textToBeWrittenInConsole;

            public string GetLastTextWrittenInConsole
            {
                get
                {
                    return this.textToBeWrittenInConsole;
                }
            }

            public void WriteLine(string text)
            {
                this.textToBeWrittenInConsole = text;
            }
        } 


        public class MyBusinessClassMock :MyBusinessClass
        {
            private ConsoleStub consoleStub;
            public MyBusinessClassMock()
            {
                // Constructor
            }

            public MyBusinessClassMock(ConsoleStub stub)
            {
                this.consoleStub = stub;
            }

            public override void MyBusinessMethod(string s)
            {
                // if MOCK is not an option then pass this stub 
                // as property or parameter in constructor 
                // if you do not want to change the api  still want
                // to pass in main class then , make it protected and 
                // then inherit it and make just a property for consoleStub

                base.MyBusinessMethod(s);
                this.consoleStub.WriteLine(s);
            }
        }

        [TestClass]
        public class ConsoleTest
        {
            private ConsoleStub consoleStub;
            private MyBusinessClassMock  mybusinessObj

            [TestInitialize]
            public void Initialize()
            {
               consoleStub = new ConsoleStub();
               mybusinessObj = new MyBusinessClassMock(consoleStub);
            }
            [TestMethod]
            public void TestMyBusinessMethod()
            {
                mybusinessObj.MyBusinessMethod("hello world");
                Assert.AreEqual(this.consoleStub.GetLastTextWrittenInConsole,"hello world" );
            }
        }

    }

// Approach - 2 
[TestClass]
    public class ConsoleTest
    {
        private ConsoleStub consoleStub;
        private MyBusinessClass  mybusinessObj

        [TestInitialize]
        public void Initialize()
        {
           consoleStub = new ConsoleStub();
           mybusinessObj = new MyBusinessClass(consoleStub);
        }
        [TestMethod]
        public void TestMyBusinessMethod()
        {
            mybusinessObj.MyBusinessMethod("hello world");
            Assert.AreEqual(this.consoleStub.GetLastTextWrittenInConsole,"hello world" );
        }
    }


回答11:

Visual Studio For Mac

None of the other solutions worked on VS for Mac

If you are using NUnit, you can add a small .NET Console Project to your Solution, then reference the project you wish to test in the References of that new Console Project.

Whatever you were doing in your [Test()] methods can be done in the Main of the console app in this fashion:

class MainClass
{
    public static void Main(string[] args)
    {
        Console.WriteLine("Console");

        // Reproduce the Unit Test
        var classToTest = new ClassToTest();
        var expected = 42;
        var actual = classToTest.MeaningOfLife();
        Console.WriteLine($"Pass: {expected.Equals(actual)}, expected={expected}, actual={actual}");
    }
}

You are free to use Console.Write and Console.WriteLine in your code under these circumstances.



回答12:

IMHO Output message are relevant only for Failed Test case in most cases. I made up the below format, you can make your own too. This is displayed in VS Test Explorer Window itself.

How to throw this message in VS Test Explorer Window? A sample code like this should work.

if(test_condition_fails)
    Assert.Fail(@"Test Type: Positive/Negative.
                Mock Properties: someclass.propertyOne: True
                someclass.propertyTwo: True
                Test Properties: someclass.testPropertyOne: True
                someclass.testPropertyOne: False
                Reason for Failure: The Mail was not sent on Success Task completion.");

You can have a separate class dedicated to this for you. Hope it helps!