如何在Visual Studio中创建一个C语言单元测试

344 阅读9分钟

任何时候,你在编写一些软件的时候,可能经常发现自己达到了一定的程度,然后构建和运行程序,看看事情是否正常。随着代码越堆越多,自己运行和检查结果变得非常耗时。在C#和Visual Studio中,可以通过单元测试的方式来设置自动测试。单元测试就是开发者写一些C#代码,以自动化的方式测试其他C#代码。在本教程中,我们将看一下在Visual Studio中设置单元测试的基本知识。


单元测试项目(.NET框架)

在Visual Studio中,有一种项目类型是专门用于创建测试的。这被恰当地命名为单元测试项目。我们希望使用这种类型的项目来创建测试,以验证我们建立的类是否按预期工作。单元测试项目类型用我们编写的测试代码创建一个新的程序集。这个新的程序集将引用另一个带有被测试代码的程序集。如果我们想验证我们的StockPortfolio.cs类是否正常工作,我们就创建一个新的测试项目,并编写一些测试代码,该测试项目将引用带有StockPortfolio的程序集。让我们在我们的解决方案中添加一个新的项目来展示这个技术。首先,在解决方案资源管理器窗口中右键点击解决方案的名称。这将弹出一个新的子菜单,你可以选择添加->新项目。
new project from solution explorer

在那里,你将选择Visual C#区域下的测试选项,选择单元测试项目(.NET框架),并给新项目起个名字。
add new project unit test

点击确定后,你会注意到Visual Studio的解决方案资源管理器中现在有两个项目。原来的那个,和刚刚创建的新测试项目。
new test project in solution explorer

检查新的Stocks.Test项目,会发现一个新的名为UnitTest1.cs的类文件,它为你设置了这个模板代码。

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Stocks.Test
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }
}

运行测试

现在,当你构建解决方案时,所发生的是解决方案中包含的两个项目同时被构建。在我们的例子中,这意味着Stocks和Stocks.Test项目都会被构建。现在让我们通过点击Ctrl+Shift+B来构建它。果然,这就是我们得到的输出。

1>------ Build started: Project: Stocks, Configuration: Debug Any CPU ------
2>------ Build started: Project: Stocks.Test, Configuration: Debug Any CPU ------
2>  Stocks.Test -> C:UsersusersourcereposStocksStocks.TestbinDebugStocks.Test.dll
1>  Stocks -> C:UsersusersourcereposStocksStocksbinDebugStocks.exe
========== Build: 2 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

所以我们现在有一个Stocks.exe程序集和一个Stocks.Test.dll程序集。我们在程序集教程中了解了不同类型的程序集。我们甚至可以马上运行测试。我们还没有测试任何有效逻辑的测试,但最初的模板代码实际上是一个测试。我们可以选择Test->Run->All Tests来看看会发生什么。
visual studio test run all tests
输出窗口向我们展示了Visual Studio正在做什么。

[2/11/2019 11:19:41 AM Informational] ------ Discover test started ------
[2/11/2019 11:19:46 AM Informational] ========== Discover test finished: 1 found (0:00:04.8223994) ==========
[2/11/2019 11:19:46 AM Informational] ------ Run test started ------
[2/11/2019 11:19:49 AM Informational] ========== Run test finished: 1 run (0:00:02.5658109) ==========

然后,在visual studio的GUI中,我们可以看到一个新的窗口,测试资源管理器窗口。
test explorer window visual studio

在测试资源管理器中,有一个名为TestMethod1的方法。 我们可以看到一个绿色的复选标记,这一定意味着好消息。嗯,这意味着测试通过了!它通过了,因为我们还没有真正测试任何功能,但我们很快就会。


单元测试的结构

检查Stocks.Test项目,我们看到一个名为UnitTest1.cs的文件。在该文件中,有一个同名的类,UnitTest1。最后,我们看到该类中有一个名为TestMethod1的方法。 刚才我们看了为我们制作的模板代码。在那里我们看到了一种奇特的语法,我们看到了*[TestClass][TestMethod]*。这些是什么?

这些括号里的名字告诉测试运行器哪些类和方法实际上是测试。这些应该在测试过程中被执行,看它们是否通过或失败,方括号中的语法在C#中被称为属性。属性是一块与类或方法相关的数据。它们可以用来做很多事情,但在设置测试时,重要的是要记住,当写一个测试运行器将为你运行的测试时,你需要把代码放在一个有TestMethod属性的方法里面,这个属性周围使用方括号。此外,测试方法所在的类必须是一个具有TestClass属性的公共类。


介绍断言

Assert类是你可以开始处理实际逻辑的地方。你可以使用Assert类来对程序中的数据进行断言。如果这些断言不是真的,那么测试将以错误的方式失败。Assert类有大量的测试方法,如AreEqual, AreNotEqual, AreNotSame, AreSame, Equals, Fail, Inconclusive, IsFalse, IsInstanceOfType, IsNotInstanceOfType, IsNotNull, IsNull, IsTrue, ReplaceNullChars, ThrowsException, and ThrowsExceptionAsync。让我们试一试。我们将尝试断言1等于2。

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Stocks.Test
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            Assert.AreEqual(1, 2);
        }
    }
}

现在我们可以运行所有的测试,当然,Visual Studio显示我们有失败的测试。
visual studio unit test failing

一旦你看到失败的测试,你可以直接钻研失败的测试方法,并获得一些相关信息。就在这里,我们点击TestMethod1来获得*Message.*AreEqual的信息。*Assert.AreEqual失败。预期:<1>。实际:<2>。*因此,测试清楚地说明了什么是预期的,以及实际发生了什么。你也可以在测试资源管理器窗口中双击失败的方法,visual studio会自动将光标移动到测试代码中发生失败的位置。
double click to move to method

现在让我们让这个测试通过。我们可以断言,1等于1。当我们现在运行所有的测试时,我们完全希望这个测试能够通过。

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Stocks.Test
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            Assert.AreEqual(1, 1);
        }
    }
}

TestMethod1 Passing


测试StockPortfolio类

好了,上面的信息让我们开始运行如何在Visual Studio中实际创建一个测试。我们现在知道了如何在解决方案中为测试目的创建一个新的项目,如何用测试属性语法识别测试,以及如何运行测试。酷。现在让我们做一些测试,确定我们项目中的StockPortfolio类是否正常工作。我们可以通过右击Stocks.Tests项目并选择添加一个类来实现。我们可以把它命名为StockPortfolioTests。
new class test file

这将创建一些模板测试代码,但我们仍然要用[TestClass]属性将该类标记为测试类,要做到这一点,Visual Studio会提示你导入Microsoft.VisualStudio.TestTools.UnitTesting命名空间。
Quick Actions Tooltip

现在我们的代码已经准备好开始使用Microsoft.VisualStudio.TestTools.UnitTesting命名空间添加测试。

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Stocks.Test
{
    [TestClass]
    public class StockPortfolioTests
    {

    }
}

引用和访问修改器

为了测试StockPortfolio类,我们需要在Stocks.Test项目中使用它。当我们试图使用它时,我们得到这个错误。"名称'StockPortfolio'在当前环境中不存在"。
The name stockportfolio does not exist in the current context

好吧,那么发生了什么?即使我们的两个项目在同一个解决方案中,我们也无法使用StockPortfolio,除非从Stocks.Test项目到Stocks建立一个引用。
我们可以通过右击References->Add Reference->Projects,然后选择Stocks来解决这个问题。这样我们就可以引用另一个项目所创建的装配,即Stocks装配。一旦这样做了,我们就会看到Stocks组件出现在Solution Explorer中。
reference assembly from different project same solution

这很好,但现在我们得到一个不同的错误信息:"StockPortfolio由于其保护级别而无法访问"。
stockportfolio is inaccessible due to its protection level

为了解决这个问题,我们可以改变StockPortfolio类的访问修改器。在创建一个类时,如果你没有指定访问级别,其默认值将被设置为 internal.一个内部类只能被同一项目内的代码使用。如果我想让StockPortfolio在StockPortfolioTests中可用,就需要将其设置为public

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Stocks
{
    public class StockPortfolio
    {
        public StockPortfolio()
        {
            stocks = new List<float>();
        }

        public StockStatistics ComputeStatistics()
        {
            StockStatistics stats = new StockStatistics();


            float sum = 0;
            foreach (float stock in stocks)
            {
                stats.HighestStock = Math.Max(stock, stats.HighestStock);
                stats.LowestStock = Math.Min(stock, stats.LowestStock);
                sum += stock;
            }

            stats.AverageStock = sum / stocks.Count;
            return stats;
        }

        public void AddStock(float stock)
        {
            stocks.Add(stock);
        }

        private List<float> stocks;
    }
}

这很好,但现在我们在这个类中得到一个错误。"不一致的可访问性:返回类型'StockStatistics'比方法'StockPortfolio.ComputeStatistics()'更不可访问"。
inconsistent accessibility return type stockstatistics is less accessible than method stockportfolio-computestatistics

这是为什么呢?这是因为StockPortfolio类利用了另一个类StockStatistics,而且它的访问修改器也是内部的。我们可以在StockStatistics类中解决这个问题,像这样把它变成公共的。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Stocks
{
    public class StockStatistics
    {
        public StockStatistics()
        {
            HighestStock = 0;
            LowestStock = float.MaxValue;
        }

        public float AverageStock;
        public float HighestStock;
        public float LowestStock;
    }
}

现在,这应该可以解决我们创建一个有用的测试所需要的一切。我们可以设计一个测试,以确保当我们给StockPortfolio提供一个股票价格集合时,它将正确计算并返回我们要求的最低股票价格。下面是测试的代码。

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Stocks.Test
{
    [TestClass]
    public class StockPortfolioTests
    {
        [TestMethod]
        public void ComputesLowestPrice()
        {
            StockPortfolio portfolio = new StockPortfolio();

            portfolio.AddStock(100);
            portfolio.AddStock(25);

            StockStatistics result = portfolio.ComputeStatistics();

            Assert.AreEqual(25, result.LowestStock);
        }
    }
}

让我们看一下。

  • 一个新的StockPortfolio对象被实例化并放置在 portfolio变量中。
  • 使用该变量,我们现在调用AddStock()方法两次。一次是添加一个100的价格,另一次是添加一个25的价格。
  • 同样从投资组合变量中,我们现在调用ComputeStatistics()方法,并将结果存储在 result变量中。
  • 然后我们可以断言,25等于调用result.LowestStock。

看起来测试通过了!

从这里开始,我们可以继续添加我们喜欢的测试,并根据需要提供正确的代码覆盖量。


如何在Visual Studio中创建一个C#单元测试 摘要

在这个关于C#测试的教程中,我们首先回顾了C#在Visual Studio中构建解决方案时如何创建一个程序集。创建的程序集与项目名称相同,扩展名为.exe或.dll。前者是一个可执行文件,而后者是一个类库。为了验证你的代码是否按照你的期望执行,你可以在Visual Studio中创建和运行单元测试。单元测试是将一个程序分解成孤立的动作的过程,你可以作为单独的单元进行测试。测试浏览器是我们可以在Visual Studio中查看我们的测试和运行它们的地方。为了利用可用于测试的方法,我们利用了Microsoft.VisualStudio.TestTools.UnitTesting命名空间。无论何时对程序中的代码进行修改,重新运行单元测试是明智的。这将确保新的代码不影响现有的代码,并且程序继续按预期工作。