我可以在单元测试中写入控制台吗?如果是,为什么控制台窗口没有打开?

时间:2012-06-26 14:28:51

标签: c# .net visual-studio unit-testing console-application

我在Visual Studio中有一个测试项目。我使用Microsoft.VisualStudio.TestTools.UnitTesting。

我在我的一个单元测试中添加了这一行:

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

当我运行测试时,测试通过,但控制台窗口根本没有打开。

有没有办法让控制台窗口可以通过单元测试进行交互?

12 个答案:

答案 0 :(得分:142)

有人在VS2013中评论了这个显然是新的功能。我一开始并不确定他的意思,但现在我知道,我认为它应该得到自己的答案。

我们可以正常使用Console.WriteLine并显示输出,但不是在输出窗口中,而是在我们点击"输出"之后在新窗口中显示。在测试细节中。

enter image description here

答案 1 :(得分:109)

注意:以下原始答案适用于通过VS2012的任何版本的VS. VS2013似乎不再具有“测试结果”窗口。相反,如果您需要特定于测试的输出,可以使用@ Stretch的Trace.Write()建议将输出写入“输出”窗口。


Console.Write方法不会写入“控制台” - 它会写入连接到正在运行的进程的标准输出句柄的任何内容。类似地,Console.Read从连接到标准输入的任何内容读取输入。

当您通过VS2010运行单元测试时,标准输出将被测试工具重定向并存储为测试输出的一部分。您可以通过右键单击“测试结果”窗口并将名为“输出(StdOut)”的列添加到显示中来查看。这将显示写入stdout的任何内容。

可以手动打开一个控制台窗口,使用P / Invoke,如@ sinni800所说。通过阅读AllocConsole文档,该函数似乎将重置stdinstdout句柄以指向新的控制台窗口。 (我不是百分百肯定的;如果我已经重定向stdout用于从我那里窃取它,我似乎有点不对,但我还没有尝试过。)

总的来说,我认为这是一个坏主意;如果您只想使用控制台来转储有关单元测试的更多信息,那么输出就在那里。继续使用Console.WriteLine,并在完成后在“测试结果”窗口中检查输出结果。

答案 2 :(得分:30)

您可以使用此行写入Visual Studio的Output Window

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

希望有所帮助

答案 3 :(得分:27)

如上所述,单元测试旨在无需交互即可运行。

但是,您可以调试单元测试,就像任何其他代码一样。最简单的方法是使用“测试结果”选项卡中的“调试”按钮。

能够调试意味着能够使用断点。因此,能够使用断点意味着能够使用 Tracepoints ,这在我每天调试中都非常有用。

基本上,Tracepoints允许您写入“输出”窗口(或更准确地说,写入标准输出)。 (可选)您可以继续运行,也可以像常规断点一样停止运行。这为您提供了"功能"您要求的,无需重建代码,或填写调试信息。

只需添加断点,然后右键单击该断点即可。选择"何时击中..."选项: When Hit option

这会打开对话框: When Breakpoint Is Hit

有几点需要注意:

  1. 请注意,断点现在显示为菱形,而不是球体,表示跟踪点
  2. 您可以像{this}一样将变量的值括起来输出变量的值。
  3. 取消选中"继续执行"复选框以使代码中断,就像任何常规断点一样
  4. 您可以选择运行宏。请小心 - 您可能会产生有害的副作用。
  5. 有关详细信息,请参阅文档。

答案 4 :(得分:6)

您可以使用

Trace.WriteLine() 

在调试unittest时写入Output窗口。

答案 5 :(得分:4)

在Visual Studio 2017中," TestContext"没有显示输出链接到测试资源管理器。 但是,Trace.Writeline()显示了输出链接。

答案 6 :(得分:3)

首先,单元测试是通过设计,完全没有交互运行。

除此之外,我认为没有人想到这种可能性。

您可以尝试使用AllocConsole P/Invoke进行攻击,即使您当前的应用程序是GUI应用程序,也会打开控制台。然后Console类将发布到现在打开的控制台。

答案 7 :(得分:3)

也可以使用Debug.WriteLine()。

答案 8 :(得分:2)

这不是一个解决方案,而是来自书中的方法

  

Roy Osherove的单元测试艺术

我们需要存根来破坏这些依赖关系,比如写入FileSystem或写入事件日志或写入控制台 -

Stub可以传入Main Class,如果stub不为null,则写入Stub。但是它可以改变api(就像现在构造函数有一个stub作为参数)。另一种方法是继承和创建Mock对象。如下所述。

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

答案 9 :(得分:2)

在大多数情况下,IMHO输出消息仅与失败的测试用例相关。我编造了以下格式,您也可以自己制作。这显示在VS Test Explorer窗口本身中。

如何在VS Test Explorer窗口中抛出此消息? 这样的示例代码应该可以工作。

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.");

您可以为此设置一个单独的班级。希望对您有帮助!

答案 10 :(得分:1)

Visual Studio Mac版

其他解决方案均未在Mac上的VS上运行

如果您使用的是 NUnit ,则可以在解决方案中添加一个小型.NET 控制台项目,然后在<该新 Console Project 的strong>参考。

您在[Test()]方法中所做的任何操作都可以通过以下方式在控制台应用程序的Main中完成:

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

在这种情况下,您可以在代码中自由使用Console.WriteConsole.WriteLine

答案 11 :(得分:1)

我有一个更简单的解决方案(由于种种懒惰原因,我最近使用了自己)。将此方法添加到您正在使用的类中:

public static void DumbDebug(string message)
{
    File.WriteAllText(@"C:\AdHocConsole\" + message + ".txt", "this is really dumb. I wish Microsoft had more obvious solutions to its solutions problems.");
}

然后...打开目录AdHocConsole,并按创建时间排序。确保在添加“打印报表”时。尽管它们是截然不同的,否则会杂耍。