作者:[美]Adam Freeman      来源:《精通ASP.NET MVC 4》

3.Visual Studio 的单元测试

有很多.NET单元测试包,其中很多是开源和免费的。本文打算使用 Visual Studio 附带的内建单元测试支持,但其他一些.NET单元测试包也是可用的。

为了演示Visual Studio的单元测试支持,本例打算对示例项目添加一个 IDiscountHelper 接口的新实现。 在 Models 文件夹下新建类文件 MinimumDiscountHelper.cs :

namespace EssentiaTools.Models
{public class MinimumDiscountHelper:IDiscountHelper{public decimal ApplyDiscount(decimal totalParam){throw new NotImplementedException();}}
}

此例的的目标是让 MinimumDiscountHelper 演示以下行为:

· 总额大于 $100时,折扣为10%

· 总额介于(并包括)$10~$100之间时,折扣为$5

· 总额小于$10时,无折扣

· 总额为负值时,抛出 ArgumentOutOfRangeException

3.1 创建单元测试项目

承接  【MVC 4】3.MVC 基本工具(创建示例项目、使用 Ninject) 的项目“EssentiaTools”,右击解决方案资源管理器中的顶级条目,从弹出的菜单中选择“Add New Project(新建项目)”

在弹出的对话框中,添加“Unit Test Project(单元测试项目)”,将项目名设置为EssentiaTools.Tests

然后对这一测试项目添加一个引用,以便能够对MVC 项目中的类执行测试。

3.2 创建单元测试

在 Essential.Tests 项目的 UnitTest1.cs 文件中添加单元测试:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EssentiaTools.Models;namespace EssentiaTools.Tests
{[TestClass]public class UnitTest1{private IDiscountHelper getTestObject(){return new MinimumDiscountHelper();}[TestMethod]public void Discount_Above_100(){//准备IDiscountHelper target = getTestObject();decimal total = 200;//动作var discountedTotal = target.ApplyDiscount(total);//断言Assert.AreEqual(total * 0.9M, discountedTotal);}}
}

只添加了一个单元测试。含有测试的类是用 TestClass 注解属性进行注释的,其中的各个测试都是用 TestMethod 注解属性进行注释方法。并不是单元测试类中的所有方法都是单元测试。例如 getTestObject 方法因为该方法没有 TestMethod 注解属性,故 Visual Studio 不会把它当作一个单元测试。

可以看出,单元测试方法遵循了“准备/动作/断言(A/A/A)”模式。

上述测试方法是通过调用 getTestObject 方法建立起来的,getTestObject 方法创建了一个待测试的实例 —— 本例为 MinimumDiscountHelper 类。另外还定义了要进行检查的 total 值,这是单元测试的“准备(Arrange)” 部分。

对于测试的“动作(Act)”部分,调用 MinimumDiscountHelper.AppleDiscount 方法,并将结果赋给 discountedTotal 变量。最后,对于测试的“断言(Assert)”部分使用了 Assert.AreEqual 方法,以检查从 AppleDiscount 方法得到的值是最初总额的90% 。

Assert 类有一系列可以在测试中使用的静态方法。这个类位于 Microsoft.VisualStudio.TestTools.UnitTesting 命名空间,该命名空间还包含了一些对建立和执行测试有用的其他类。有关该命名空间的类,可以参阅:https://msdn.microsoft.com/en-us/library/ms182530.aspx

Assert 类是用的最多的一个,其中重要的一些方法如下:

Assert 类中的每一个静态方法都可以检查单元测试的某个方面。如果断言失败,将抛出一个异常,这意味着整个单元测试失败。由于每一个单元测试都是独立进行处理的,因此其他单元测试将被继续执行。

上述的每一个方法都有一个string 为参数的重载,该字符串作为断言失败时的消息元素。 AreEqual 和 AreNotEqual 方法有几个重载,以满足特定类型的比较。例如,有一个版本可以比较字符串, 而不需要考虑其他情况。

提示:Microsoft.VisualStudio.TestTools.UnitTesting 命名空间中一个值得注意的成员是 ExpectedException 属性。这是一个断言,只有当单元测试抛出 ExceptionType 参数指定类型的异常时,该断言才是成功的。这是一种确保单元测试抛出异常的整洁方式,而不需要在单元测试中构造 try..catch 块

为了验证前述 MinimumDiscountHelper 的其他行为,修改文件 UnitTest1.cs 如下:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EssentiaTools.Models;namespace EssentiaTools.Tests
{[TestClass]public class UnitTest1{private IDiscountHelper getTestObject(){return new MinimumDiscountHelper();}[TestMethod]public void Discount_Above_100(){//准备IDiscountHelper target = getTestObject();decimal total = 200;//动作var discountedTotal = target.ApplyDiscount(total);//断言Assert.AreEqual(total * 0.9M, discountedTotal);}  [TestMethod]public void Discount_Between_10_And_100(){//准备IDiscountHelper target = getTestObject();//动作decimal TenDollarDiscount = target.ApplyDiscount(10);decimal HundredDollarDiscount = target.ApplyDiscount(100);decimal FiftyDollarDiscount = target.ApplyDiscount(50);//断言Assert.AreEqual(5, TenDollarDiscount, "$10 discount is wrong");Assert.AreEqual(95, HundredDollarDiscount, "$100 discoutn is wrong");Assert.AreEqual(45, FiftyDollarDiscount, "$50 discount is wrong");}[TestMethod]public void Discount_Less_Than_10(){IDiscountHelper target = getTestObject();decimal discount5 = target.ApplyDiscount(5);decimal discount0 = target.ApplyDiscount(0);Assert.AreEqual(5, discount5);Assert.AreEqual(0, discount0);}[TestMethod][ExpectedException(typeof(ArgumentOutOfRangeException))]public void Discount_Negative_Total(){IDiscountHelper target = getTestObject();target.ApplyDiscount(-1);}}
}

3.3 运行单元测试(并失败)

Visual Studio 2012 为管理和运行测试引入了一个更为有用的“Test Explorer(测试资源管理器)”窗口。从 Visual Studio 的“Test(测试)”菜单中选择“Window(窗口)”—>"Test Explorer(测试资源管理器)",便可以看到这一新窗口,点击左上角附近的“RunAll(全部运行)”按钮,会看到下图效果:

可以在该窗口的左侧面板中看到所定义的测试列表。所有的测试都失败了,这是当然的,因为所测试的这些方法还未实现。可以点其中任意测试,测试失败的原因和细节会显示在窗口的右侧面板中。

3.4 实现特性

现在,到了实现特性的时候了。当编码工作完成时,基本上可以确信代码是能够按预期工作的。有了之前的准备,MinimumDiscountHelper 类的实现相当简单:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;namespace EssentiaTools.Models
{public class MinimumDiscountHelper : IDiscountHelper{public decimal ApplyDiscount(decimal totalParam){if (totalParam < 0){throw new ArgumentOutOfRangeException();}else if (totalParam > 100){return totalParam * 0.9M;}else if (totalParam > 10 && totalParam <= 100){return totalParam - 5;}else{return totalParam;}}}
}

3.5 测试并修正代码

为了演示如何利用 Visual Studio 进行单元测试迭代,上述代码故意留下了一个错误。如果点击“测试资源管理器”窗口中的“全部运行”按钮,则可以看到该错误的效果。测试结果如下:

可以看到,三个单元测试得到了通过,但 Discount_Between_10_And_100 测试方法检测到了一个问题。当点击这一失败的测试时,可以看到测试期望得到的是5,但实际得到的是10。

此刻,重新审视代码便会发现,并未得到适当的实现——特别是总额是10或100的折扣,未做适当处理。问题出在 MinimumDiscountHelper 类的这句语句上:

...
else if (totalParam > 10 && totalParam <= 100)
...

虽然目标是建立介于(包括)$10~$100 直接的行为,但实际却排除了等于$10 的情况,修改成:

...
else if (totalParam >= 10 && totalParam <= 100)
...

重新运行测试,所有测试代码都已通过:

4. 使用 Moq

前面的单元测试如此简单的原因之一是因为测试的是一个不依赖于其他类而起作用的单一的类。当然,实际项目中有这样的类,但往往还需要测试一些不能孤立运行的对象。在这些情况下,需要将注意力于感兴趣的类或方法上,才能不必对依赖类也进行隐式测试。

一个有用的办法是使用模仿对象,它能够以一种特殊而受控的的方式,来模拟项目中实际对象的功能。模仿对象能够缩小测试的侧重点,以使用户只检查感兴趣的功能。

4.1 理解问题

在开始使用 Moq 之前,本例想演示一个试图要修正的问题。下面打算对 LinqValueCalculator 类进行单元测试,LinqValueCalculator 在前面出现过,具体代码为:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;namespace EssentiaTools.Models
{public class LinqValueCalculator : IValueCalculator{private IDiscountHelper discounter;public LinqValueCalculator(IDiscountHelper discountParam){discounter = discountParam;}public decimal ValueProducts(IEnumerable<Product> products){return discounter.ApplyDiscount(products.Sum(p => p.Price));}}
}

为了,测试这个类,在单元测试项目中新增单元测试文件 UnitTest2.cs :

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EssentiaTools.Models;
using System.Linq;namespace EssentiaTools.Tests
{[TestClass]public class UnitTest2{private Product[] products = {new Product{Name="Kayak",Catogory="Watersports",Price=275M},new Product{Name="Lifejacket",Catogory="Watersports",Price=48.95M},new Product{Name="Soccer ball",Catogory="Soccer",Price=19.50M},new Product{Name="Corner flag",Catogory="Soccer",Price=34.95M}};[TestMethod]public void Sum_Products_Correctly(){//准备var discounter = new MinimumDiscountHelper();var target = new LinqValueCalculator(discounter);var goalTotal = products.Sum(e => e.Price);//动作var result = target.ValueProducts(products);//断言
            Assert.AreEqual(goalTotal, result);}}
}

现在面临的问题是,LinqValueCalculator 类依赖于 IDiscountHelper 接口的实现才能进行操作。此例使用了 MinimumDiscountHelper 类(这是 IDiscountHelper 接口的实现类),它表现了两个不同的问题。

第一个问题是单元测试变得复杂和脆弱。为了创建一个能够进行工作的单元测试,需要考虑 IDiscountHelper 实现中的折扣逻辑,以便判断出 ValueProducts 方法的预期值。脆弱来自这样一个事实:一旦该实现中的折扣逻辑发生变化,测试便会失败。

第二个也是最令人担忧的问题是已经延展了这一单元测试的范围,使它的隐式的包含了 MinimumDiscountHelper 类。当单元测试失败时,用户不知道问题是出在 LinqValueCalculator 类中,还是在 MinimumDiscountHelper 类中。

当单元测试简单且焦点集中时,会工作的很好,而当前的设置会让这两个特征都不能得到满足。而在MVC项目中添加并运用 Moq ,能够避免这些问题。

4.2 将 Moq 添加到VisualStudio 项目

和前面的 Ninject 一样,在测试项目中 搜索并添加 NuGet 程序包 Moq 。

4.3 对单元测试添加模仿对象

对单元测试添加模仿对象,其目的是告诉 Moq,用户想使用哪一种对象。对它的行为进行配置,然后将该对象运用于测试目的。

在单元测试中使用 Mock 对象,为 LinqValueCalculator 的单元测试添加模仿对象,修改 UnitTest2.cs 文件:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EssentiaTools.Models;
using System.Linq;
using Moq;namespace EssentiaTools.Tests
{[TestClass]public class UnitTest2{private Product[] products = {new Product{Name="Kayak",Catogory="Watersports",Price=275M},new Product{Name="Lifejacket",Catogory="Watersports",Price=48.95M},new Product{Name="Soccer ball",Catogory="Soccer",Price=19.50M},new Product{Name="Corner flag",Catogory="Soccer",Price=34.95M}};[TestMethod]public void Sum_Products_Correctly(){//准备  Mock<IDiscountHelper> mock = new Mock<IDiscountHelper>();mock.Setup(m => m.ApplyDiscount(It.IsAny<decimal>())).Returns<decimal>(total => total);var target = new LinqValueCalculator(mock.Object);//动作var result = target.ValueProducts(products);//断言Assert.AreEqual(products.Sum(e => e.Price), result);}}
}

第一次使用 Moq 时,可能会觉得其语法有点奇怪,下面将演示该过程的每个步骤。

(1) 创建模仿对象

第一步是要告诉 Moq,用户想使用的是哪种模仿对象。 Moq 十分依赖于泛型的类型参数,从以下语句可以看到这种参数的使用方式,这是告诉 Moq,要模仿的对象时 IDiscountHelper 实现。

...
Mock<IDiscountHelper> mock = new Mock<IDiscountHelper>();
...

创建一个强类型的的 Mock<IDiscountHelper> 对象,目的是告诉 Moq 库,它要处理的是哪种类型——当然,这便是用于该单元测试的 IDiscountHelper 接口。单为了改善单元测试的侧重点,这可以是想要隔离出来的任何类型。

(2) 选择方法

除了创建强类型的Mock对象外,还需要指定它的行为方式——这是模仿过程的核心,它可以建立模仿所需要的基准行为,用户可以将这种行为用于对单元测试中目标对象的功能进行测试。以下是单元测试中的语句,它为模仿对象建立了用户所希望的行为。

...mock.Setup(m => m.ApplyDiscount(It.IsAny<decimal>())).Returns<decimal>(total => total);
...

用 Setup 方法给模仿对象添加一个方法。 Moq 使用 LINQ 和 lambda 表达式进行工作。在调用 Setup 方法时,Moq 会传递要求它的接口。它巧妙地封装了一些本书不打算细说的LINQ 魔力,这种魔力让用户可以选择想要通过 lambda 表达式进行配置或检查的方法。对于该单元测试,希望定义 AppleDiscount 方法的行为,它是 IDiscountHelper 接口的唯一方法,也是对 LinqValueCalculator 类进行测试所需要的方法。

必须告诉 Moq 用户感兴趣的参数值是什么,这是要用 It 类要做的事情,如以下加粗部分所示。

...mock.Setup(m => m.ApplyDiscount(It.IsAny<decimal>())).Returns<decimal>(total => total);
...

这个It 类定义了许多以泛型类型参数进行使用的方法。此例用 decimal 作为泛型类型调用了 IsAny 方式。这是告诉 Moq ,当以任何十进制为参数来调用 ApplyDiscount 方法时,它应该运用我们定义的这一行为。
下面给出了 It 类所提供的方法,所有的这些方法都是静态的。

(3) 定义结果

Returns 方法让用户指定在调用模仿方法时要返回的结果。其类型参数用以指定结果的类型,而用 lambda 表达式来指定结果。如下:

...mock.Setup(m => m.ApplyDiscount(It.IsAny<decimal>())).Returns<decimal>(total => total);...

通过调用带有 decimal 类型参数的 Returns 方法(即 Returns<decimal>),这是告诉 Moq 要返回一个十进制的值。对于 lambda 表达式,Moq 传递了一个在ApplyDiscount 方法中接收的类型值 —— 此例创建了一个穿透方法,该方法返回了传递给模仿的 ApplyDiscount 方法的值,并未对这个值执行任何操作。

上述过程的思想是:

为了对 LinqValueCalculator 进行单元测试,如果创建一个 IDiscountHelper 模仿对象,便可以在单元测试中排除 IDiscountHelper 接口的实现类 MinimumDiscountHelper ,从而使单元测试更为简单容易。用 Moq 创建模仿对象的整个过程包括了以下几个步骤:a. 用 Mock 创建模仿对象; b. 用Setup 方法建立模仿对象的行为; c. 用 It 类设置行为的参数; d. 用Return 方法指定行为的返回类型; e. 用 lambda 表达式在Return 方法中建立具体行为。

(4) 使用模仿对象

最后一个步骤是在单元测试中使用这个模仿对象,通过读取 Mock<IDiscountHelper> 对象的Object 属性值来实现

...
var target = new LinqValueCalculator(mock.Object);
...

总结下,在上述示例中,Object 属性返回 IDiscountHelper 接口的实现,该实现中的 ApplyDiscount 方法返回它传递的十进制参数的值。

这使单元测试很容易执行,因为用户可以自行求取 Product 对象的价格总和,并检查 LinqValueCalculator 对象得到了相同的值。

...Assert.AreEqual(products.Sum(e => e.Price), result);
...

以这种方式使用 Moq 的好处是,单元测试只检查 LinqValueCalculator 对象的行为,并不依赖任何 Models 文件夹中 IDiscountHelper 接口的真实实现。这意味着当测试失败时,用户便知道问题出在 LinqValueCalculator 实现中,或建立模仿对象的方式中。而解决源自这些方面的问题,比处理实际对象链及其相互交互,要更叫简单而容易。

4.4 创建更复杂的模仿对象

前面展示了一个十分简单的模仿对象,但 Moq 最漂亮的部分是快速建立复杂行为以便对不同情况进行测试的能力。在 UnitTest2.cs  中新建一个单元测试,模仿更加复杂的 IDiscountHelper 接口实现。

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EssentiaTools.Models;
using System.Linq;
using Moq;namespace EssentiaTools.Tests
{[TestClass]public class UnitTest2{private Product[] products = {new Product{Name="Kayak",Catogory="Watersports",Price=275M},new Product{Name="Lifejacket",Catogory="Watersports",Price=48.95M},new Product{Name="Soccer ball",Catogory="Soccer",Price=19.50M},new Product{Name="Corner flag",Catogory="Soccer",Price=34.95M}};[TestMethod]public void Sum_Products_Correctly(){//准备Mock<IDiscountHelper> mock = new Mock<IDiscountHelper>();mock.Setup(m => m.ApplyDiscount(It.IsAny<decimal>())).Returns<decimal>(total => total);var target = new LinqValueCalculator(mock.Object);//动作var result = target.ValueProducts(products);//断言Assert.AreEqual(products.Sum(e => e.Price), result);}private Product[] createProduct(decimal value){return new[] { new Product { Price = value } };}[TestMethod][ExpectedException(typeof(System.ArgumentOutOfRangeException))]public void Pass_Through_Variable_Discounts(){Mock<IDiscountHelper> mock = new Mock<IDiscountHelper>();mock.Setup(m => m.ApplyDiscount(It.IsAny<decimal>())).Returns<decimal>(total => total);mock.Setup(m => m.ApplyDiscount(It.Is<decimal>(v => v == 0))).Throws<System.ArgumentOutOfRangeException>();mock.Setup(m => m.ApplyDiscount(It.Is<decimal>(v => v > 100))).Returns<decimal>(total => (total * 0.9M));mock.Setup(m => m.ApplyDiscount(It.IsInRange<decimal>(10, 100, Range.Inclusive))).Returns<decimal>(total => total - 5);var target = new LinqValueCalculator(mock.Object);decimal FiveDollarDiscount = target.ValueProducts(createProduct(5));decimal TenDollarDiscount = target.ValueProducts(createProduct(10));decimal FiftyDollarDiscount = target.ValueProducts(createProduct(50));decimal HundredDollarDiscount = target.ValueProducts(createProduct(100));decimal FiveHundredDollarDiscount = target.ValueProducts(createProduct(500));Assert.AreEqual(5, FiveDollarDiscount, "$5 Fail");Assert.AreEqual(5, TenDollarDiscount, "$10 Fail");Assert.AreEqual(45, FiftyDollarDiscount, "$50 Fail");Assert.AreEqual(95, HundredDollarDiscount, "$100 Fail");Assert.AreEqual(450, FiveHundredDollarDiscount, "$500 Fail");target.ValueProducts(createProduct(0));}}
}

在单元测试期间,复制另一个模型类期望的行为似乎是在做一个奇怪的事情,但这能够完美演示 Moq 的一些不同用法。

可以看出,根据所接收到的参数值,定义了 ApplyDiscount 方法的四个不同的行为。最简单的行为是“全匹配”,它直接返回任意的decimal 值,如下:

mock.Setup(m => m.ApplyDiscount(It.IsAny<decimal>())).Returns<decimal>(total => total);

这是用于上一示例的同一行为,把它放在这是因为调用 Setup 方法的顺序会影响模仿对象的行为。Moq 会以相反的顺序评估所给定的行为,因此会考虑调用最后一个 Setup 方法。这意味着,用户必须按从最一般到最特殊的顺序,小心地创建模仿行为。 It.IsAny<decimal> 是此例所定义的最一般的条件,因而首先运用它。如果颠倒调用 Setup 的顺序,该行为将能匹配对 ApplyDiscount 方法的所有调用,并生成错误的模仿结果。

(1) 模仿特定值(并抛出异常)

对于 Setup 方法第二个调用,使用了 It.Is 方法

 mock.Setup(m => m.ApplyDiscount(It.Is<decimal>(v => v == 0))).Throws<System.ArgumentOutOfRangeException>();

若传递给 ApplyDiscount 方法的值是0,则 Is方法的谓词便返回 true。这里并未返回一个结果,而是使用了 Throws 方法,这会让 Moq 抛出一个用类型参数指定的异常实例。

示例还用 Is 方法捕捉了大于100的值:

mock.Setup(m => m.ApplyDiscount(It.Is<decimal>(v => v > 100))).Returns<decimal>(total => (total * 0.9M));

Is.It 方法是为不同参数值建立指定行为最灵活的方式,因为用户可以使用任意谓词来返回 true 或 false 。在创建复杂模仿对象的,这是最常用的方法。

(2) 模仿值的范围

It 对象最后是和 IsInRange 方法一起使用的,它让用户能够捕捉参数值的范围。

mock.Setup(m => m.ApplyDiscount(It.IsInRange<decimal>(10, 100, Range.Inclusive))).Returns<decimal>(total => total - 5);

这里介绍这一方法是出于完整性,如果是在用户自己的项目,可以使用 It 方法和一个谓词来做同样的事情,如下所示:

mock.Setup(m => m.ApplyDiscount(It.Is<decimal>(v=>v>=10&&v<=100))).Returns<decimal>(total => total - 5);

效果是相同的,但谓词方法更为灵活。Moq 有一系列非常有用的特性,阅读https://github.com/Moq/moq4/wiki/Quickstart上提供的入门指南,可以看到许多用法。

源码地址:https://github.com/YeXiaoChao/EssentiaTools

转载于:https://www.cnblogs.com/yc-755909659/p/5254427.html

【MVC 4】4.MVC 基本工具(Visual Studio 的单元测试、使用Moq)相关推荐

  1. 微软家族的首个跨平台开发工具 Visual Studio Code

    微软家族的首个跨平台开发工具 Visual Studio Code 长这样哦.很多童鞋说像 Atom,其实他们在官网就说了用的是 Electron Shell(Atom) Why Visual Stu ...

  2. Visual Studio 2008单元测试实践一

    关键字:单元测试,Visual Studio 2008 单元测试是在软件开发过程中要进行的最低级别的测试活动,在单元测试活动中,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试. 在一种传统的 ...

  3. Visual Studio 2010 单元测试之一---普通单元测试:http://blog.csdn.net/tjvictor/archive/2011/02/09/6175362.aspx...

    本文以Visual Studio 2010为例,来介绍如何在Visual Studio里面进行单元测试. 首先来介绍普通单元测试,这是进行顺序测试.压力测试的基础.如果在Visual Studio 2 ...

  4. 使用Visual Studio进行单元测试

    原文:使用Visual Studio进行单元测试 一.使用Visual Studio进行单元测试的几个建议 1.先写单元测试(依我愚见,应该是接口先行,如果有的话) -> 测试失败 -> ...

  5. HTML基础学习之(一)安装前端开发工具Visual Studio Code

    查看电脑版本 按下Win+R调出运行窗口,然后直接输入dxdiag 下载 打开官网: https://code.visualstudio.com/ 下载 安装 下一步 下一步 安装后默认是C盘 设置中 ...

  6. 【教程】 .Net环境和开发工具Visual Studio的安装

    文章目录 前言: 1. .NET环境的安装: 2. Visual Studio工具的安装: 总结: 前言: .NET 是一个免费的跨平台开放源代码开发人员平台,用于生成多种类型的应用程序. .NET ...

  7. 前端网页开发工具Visual Studio Code推荐

    前端开发有很多软件可使用,笔者用过Adobe Dreamweaver CS6 ,IntelliJ IDEA ,还有webstrom.但是这些虽然有他们的好处,比如 Dreamweaver 他特有的视图 ...

  8. node.js开发工具--visual studio code

    visual studio code,个人觉得这是开发node最好的编辑器,没有之一. 下载地址:https://code.visualstudio.com/Download 之前的版本只能开一个窗口 ...

  9. [工具]Visual Studio

    1,Tab键的使用: 如不说有这样的代码:public Member member { get; set; } 当我们编辑完Member后,按一下Tab键,就能够将光标锁定到member上,等待键盘输 ...

最新文章

  1. 程序员笔试面试后上机_2021年国考笔试成绩查询后,面试准备阶段需要做好四方面...
  2. Gartner:如何利用数字孪生帮助企业创造价值?
  3. 【每天读一遍,不久你就会变!】【送给迷茫的朋友】
  4. c语言向自定数组_C语言一维数组的定义和引用
  5. APP移动购物界面设计灵感
  6. Java中的break Label 和continue Label
  7. MapReduce:详解Shuffle过程
  8. mysql可视化界面创建表_使用可视化界面创建表
  9. 谢烟客---------Linux之深入理解anaconda使用
  10. 连接数据库报错:Access denied for user ‘root‘@ ‘*.*.*.*‘ (using password: YES)
  11. python表达式_Python的表达式写法
  12. 手札-11(京东实战手札)
  13. 计算机网络的对学生的利弊英语作文,网络的利与弊英语作文范文
  14. 设计模式简单RPG游戏设计
  15. javaScript内存溢出vue-cli3解决方案
  16. 实时弹幕系统的设计与实现
  17. 升级sp3后出现:一个问题阻止windows正确检测此机器许可证--解决方案
  18. 杜克大学计算机数据科学,美国杜克大学数据科学硕士专业课程设置有哪些?留学申请条件有哪些?...
  19. heritrix参考文献
  20. 怎么将图片转换成Word文档?手机也能轻松操作怕转

热门文章

  1. linux安装teamviewer黑屏,如何在CentOS 7上安装TeamViewer
  2. b 站账号快速升级到 Lv6:每天自动签到,观看,分享,投币视频
  3. 计算机上播放时没声音什么故障,新买的电脑插上耳机没有声音怎么办?具体故障原因及解决方法看这里...
  4. 家庭计算机如何共享打印机共享,win7系统怎么设置家庭组共享打印机
  5. Facebook如何管理150亿张照片
  6. 还不懂PID控制?精华都在这了!
  7. QT 怎么获取linux本机的IP地址?
  8. android开发网站的流程图,Android_客户端开发流程图及案例.pdf
  9. 北京,探索「宜居」的技术路径
  10. golang 将kafka的offset置为最新