65.9K
CodeProject 正在变化。 阅读更多。
Home

使用异步服务回调测试 Silverlight 应用程序的单元测试

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (6投票s)

2011 年 2 月 18 日

CPOL

13分钟阅读

viewsIcon

51638

downloadIcon

767

本文介绍了一个关于使用异步服务回调对 Silverlight 应用程序进行单元测试的示例。

引言

本文介绍了一个关于使用异步服务回调对 Silverlight 应用程序进行单元测试的示例。

背景

与其他类型的单元测试相比,Silverlight 应用程序的测试面临两个特殊的挑战。

  • Silverlight 应用程序运行在 Web 浏览器的沙盒中。其他类型的应用程序可用的某些功能可能不适用于 Silverlight 应用程序。如果我们以测试其他类型应用程序的方式来测试 Silverlight 应用程序,那么 Silverlight 应用程序可能通过单元测试,但在集成测试系统测试甚至生产环境中都会失败。
  • 如果 Silverlight 应用程序使用了任何 WCF/Web 服务,服务调用通常是异步的。回调函数部分或全部在与调用线程不同的线程中运行。因此,测试方法可能比回调函数提前完成,从而产生错误的测试结果。

本文旨在提供一个关于使用异步服务回调对 Silverlight 应用程序进行单元测试的示例,以应对这两个挑战。有关 Silverlight 应用程序单元测试的其他参考资料,您可以查阅以下内容:此链接此链接此链接

SolutionExplorer.JPG

附加的 Visual Studio 解决方案包含三个项目:

  • SilverlightApplication”项目是要测试的 Silverlight 应用程序。
  • SilverlightApplicationUnitTest”项目是测试项目。该测试项目本身就是一个特殊的 Silverlight 应用程序,用于测试“SilverlightApplication”项目。
  • SilverlightHostWebApplication”是一个 ASP.NET Web 应用程序。“SilverlightApplication”项目托管在“Default.aspx”页面上,“SilverlightApplicationUnitTest”项目托管在“TestPage.aspx”页面上。该项目还公开了一个 WCF 服务,供“SilverlightApplication”异步调用。

本 Visual Studio 解决方案使用 Visual Studio 2010 和 Silverlight 4 开发。我将首先介绍“SilverlightHostWebApplication”项目中的 WCF 服务,然后介绍“SilverlightApplication”项目。之后,我将展示如何创建测试项目“SilverlightApplicationUnitTest”,以及如何使用它来测试“SilverlightApplication”项目中的 MVVM 视图模型类中进行的异步 WCF 服务调用。

WCF服务

供“SilverlightApplication”项目调用的 WCF 服务实现在“SilverlightHostWebApplication”项目中的“StudentService.svc.cs”文件中。

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
 
namespace SilverlightHostWebApplication
{
    [DataContract]
    public class Student
    {
        [DataMember(Order = 0)]
        public string ID { get; set; }
        [DataMember(Order = 1)]
        public string Name { get; set; }
        [DataMember(Order = 2)]
        public DateTime EnrollmentDate { get; set; }
        [DataMember(Order = 3)]
        public int Score { get; set; }
        [DataMember(Order = 4)]
        public string Gender { get; set; }
    }
 
    [ServiceContract]
    public class StudentService
    {
        [OperationContract]
        public List<Student> GenerateStudents(int NoOfStudents)
        {
            // This is the correct method.
            // It returns exactly the same number of students
            // as requested.
            return CreateStudentsList(NoOfStudents);
        }
 
        [OperationContract]
        public List<Student> GenerateStudentsWithError(int NoOfStudents)
        {
            // This method has an artificial error.
            // It is used to generate a failure in the unit test.
            // The number of students generated is (NoOfStudents - 1)
            return CreateStudentsList(NoOfStudents - 1);
        }
 
        private List<Student> CreateStudentsList(int NoOfStudents)
        {
            List<Student> students = new List<Student>();
 
            Random rd = new Random();
            for (int i = 1; i <= NoOfStudents; i++)
            {
                int score = Convert.ToInt16(60 + rd.NextDouble() * 40);
                students.Add(new Student()
                {
                    ID = "ID No." + i.ToString(),
                    Name = "Student Name " + i.ToString(),
                    EnrollmentDate = DateTime.Now,
                    Score = score,
                    Gender = (score >= 80) ? "Female" : "Male"
                });
            }
 
            return students;
        }
    }
}

该 WCF 服务实现了一个数据协定Student”和一个服务协定StudentService”。在服务协定“StudentService”中,我们有两个操作协定

  • GenerateStudents”方法接受一个整数输入参数“NoOfStudents”,并生成一个“Student”对象列表返回给调用者。生成的列表中的学生数量与输入参数“NoOfStudents”完全匹配。
  • GenerateStudentsWithError”方法与“GenerateStudents”方法执行相同的操作,但会产生一个人工错误。生成的学生数量是错误的,与输入参数“NoOfStudents”不匹配。

这两个操作协定都将被“SilverlightApplication”项目通过异步 WCF 调用。我们将看到,单元测试项目“SilverlightApplicationUnitTest”可以成功调用“GenerateStudents”方法,但无法成功调用“GenerateStudentsWithError”方法。

Silverlight 应用程序

SilverlightApplication”项目是本文中要测试的 Silverlight 项目。

SolutionExplorerApplication.JPG

这个简单的应用程序是基于 MVVM 模式构建的。如果您不熟悉 MVVM,可以参考“Silverlight MVVM 应用程序的数据和命令绑定”以及其他参考资料。在查看应用程序代码之前,我们先看看它是如何运行的。将宿主 ASP.NET 应用程序“SilverlightHostWebApplication”设置为启动项目,并将“Default.aspx”页面设置为启动页,我们就可以从 Visual Studio 中启动“SilverlightApplication”。

RunApplicationCorrect.JPG

在此 Silverlight 应用程序中,我们可以看到两个按钮。每个按钮旨在触发一个 WCF 服务调用以检索 10 个学生。但是,“Get 10 students from WCF”按钮会触发对“GenerateStudents”方法的调用,而“Get 10 students from wrong method in WCF”按钮会触发对“GenerateStudentsWithError”方法的调用。

RunApplicationWrong.JPG

如上图所示,点击“Get 10 students from WCF”按钮,我们确实从 WCF 服务接收到了正确数量的学生。但是,当我们点击“Get 10 students from wrong method in WCF”按钮时,返回的学生数量是错误的。我们将看到单元测试应用程序如何捕获这个人工错误。但在我们查看单元测试应用程序之前,我们将先看看实现“SilverlightApplication”项目的代码。“SilverlightApplication”项目是一个非常简单的 Silverlight MVVM 应用程序,它只有一个模型类、一个视图模型类和一个 XAML 视图文件。在查看了帮助我们实现 Silverlight MVVM 应用程序的实用程序类之后,我将逐一介绍它们。

MVVM 实用程序类

为了简化“SilverlightApplication”中 MVVM 模式的实现,我在“Utilities\Utilities.cs”文件中创建了两个实用程序类。

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
 
namespace SilverlightApplication.Utilities
{
    public delegate void AsyncCompleted();
 
    // This class is used as the base class for all view models
    public abstract class ViewModelBase
        : DependencyObject, INotifyPropertyChanged
    {
        public event AsyncCompleted AsyncCallbackCompleted;
        public event PropertyChangedEventHandler PropertyChanged;
 
        public bool IsDesignTime
        {
            get { return DesignerProperties.IsInDesignTool; }
        }
 
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
 
        protected void InformCallbackCompleted()
        {
            if (AsyncCallbackCompleted != null)
            {
                AsyncCallbackCompleted();
            }
        }
    }
 
    // This class is used to create commands in the view models
    public class RelayCommand : ICommand
    {
        private readonly Action handler;
        private bool isEnabled;
 
        public RelayCommand(Action handler)
        {
            this.handler = handler;
        }
 
        public bool IsEnabled
        {
            get { return isEnabled; }
            set
            {
                if (value != isEnabled)
                {
                    isEnabled = value;
                    if (CanExecuteChanged != null)
                    {
                        CanExecuteChanged(this, EventArgs.Empty);
                    }
                }
            }
        }
 
        public bool CanExecute(object parameter)
        {
            return IsEnabled;
        }
 
        public event EventHandler CanExecuteChanged;
 
        public void Execute(object parameter)
        {
            handler();
        }
    }
}

ViewModelBase”类实现了INotifyPropertyChanged接口。它将用作应用程序视图模型的基础类。“RelayCommand”类实现了ICommand接口。它将用于在应用程序视图模型中创建命令对象。我写了一篇单独的文章来介绍这两个类如何帮助我们构建 Silverlight MVVM 应用程序。如果您有兴趣,可以在此处查看。

除了 MVVM 支持之外,您还应该注意到我在“ViewModelBase”类中添加了一个事件AsyncCallbackCompleted”和一个公共方法“InformCallbackCompleted”来触发此事件。为了让单元测试应用程序能够测试 Silverlight 应用程序中的异步回调函数,回调函数需要在完成之前触发此事件。您将看到我们在“SilverlightApplication”应用程序的视图模型中如何触发此事件,以及我们如何使用此事件在单元测试项目中测试回调函数。

模型类

SilverlightApplication”项目的模型类实现在“Models\StudentModel.cs”文件中。

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using SilverlightApplication.StudentService;
 
namespace SilverlightApplication.Models
{
    public class StudentModel
    {
        public void GetStudents(int NumberOfStudent,
            EventHandler<GenerateStudentsCompletedEventArgs> targetFunction)
        {
            StudentServiceClient client = new StudentServiceClient();
            client.GenerateStudentsCompleted
                += new EventHandler<GenerateStudentsCompletedEventArgs>(targetFunction);
            client.GenerateStudentsAsync(NumberOfStudent);
        }
 
        public void GetStudentsWithError(int NumberOfStudent,
            EventHandler<GenerateStudentsWithErrorCompletedEventArgs> targetFunction)
        {
            StudentServiceClient client = new StudentServiceClient();
            client.GenerateStudentsWithErrorCompleted
                += new EventHandler<GenerateStudentsWithErrorCompletedEventArgs>
			(targetFunction);
            client.GenerateStudentsWithErrorAsync(NumberOfStudent);
        }
    }
}

由于所有的学生生成功能都已在 WCF 服务中实现,因此该模型类非常简单。它只有两个方法:

  • GetStudents”方法调用 WCF 服务中的正确方法。
  • GetStudentsWithError”方法调用 WCF 服务中生成错误数量学生的方法。

每个方法都接受一个指向回调函数的引用。当 WCF 服务调用返回结果时,将调用回调函数。回调函数将在应用程序的视图模型中实现。

ViewModel

SilverlightApplication”项目的视图模型实现在“ViewModels\MainPageViewModel.cs”文件中。

using System;
using SilverlightApplication.Utilities;
using SilverlightApplication.StudentService;
using System.Collections.Generic;
using SilverlightApplication.Models;
 
namespace SilverlightApplication.ViewModels
{
    public class MainPageViewModel : ViewModelBase
    {
        // Public properties
        private List<Student> studentList = null;
        public List<Student> StudentList
        {
            get { return studentList; }
            private set
            {
                studentList = value;
                NotifyPropertyChanged("StudentList");
            }
        }
 
        // Public commands
        public RelayCommand GetStudentWCFCommand { get; private set; }
        private void GetStudentWCF()
        {
            StudentModel model = new StudentModel();
            model.GetStudents(10, (s, r) =>
            {
                StudentList = (List<Student>)r.Result;
 
                // This is used for unit testing
                // It has no effect to the normal program flow.
                InformCallbackCompleted();
            });
        }
 
        public RelayCommand GetStudentWCFWrongMethodCommand { get; private set; }
        private void GetStudentWCFWrongMethod()
        {
            StudentModel model = new StudentModel();
            model.GetStudentsWithError(10, (s, r) =>
            {
                StudentList = (List<Student>)r.Result;
 
                // This is used for unit testing
                // It has no effect to the normal program flow.
                InformCallbackCompleted();
            });
        }
 
        // Commands should be initiated in this method
        private void WireCommands()
        {
            GetStudentWCFCommand = new RelayCommand(GetStudentWCF);
            GetStudentWCFCommand.IsEnabled = true;
 
            GetStudentWCFWrongMethodCommand = new RelayCommand(GetStudentWCFWrongMethod);
            GetStudentWCFWrongMethodCommand.IsEnabled = true;
        }
 
        // Constructor
        public MainPageViewModel() : base()
        {
            WireCommands();
        }
    }
}

该视图模型有一个公共属性和两个命令。

  • StudentList”属性是从 WCF 服务检索到的学生列表。
  • GetStudentWCFCommand”命令启动对 WCF 服务中“CreateStudentsList”方法的调用。
  • GetStudentWCFWrongMethodCommand”命令启动对 WCF 服务中“GenerateStudentsWithError”方法的调用。

您应该注意匿名回调函数。它们将收到的学生列表分配给“StudentList”属性,并触发“AsyncCallbackCompleted”事件。通过触发此事件,回调函数通知单元测试项目中的测试方法,异步服务调用已完成,并且服务调用的结果已准备好供测试方法进行检查。

XAML 视图

SilverlightApplication”项目的视图实现在“MainPage.xaml”文件中。

<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
             x:Class="SilverlightApplication.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" FontFamily="Verdana" FontSize="12"
    d:DesignHeight="300" d:DesignWidth="400">
 
    <Grid x:Name="LayoutRoot" Margin="10" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        
        <Grid Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            
            <Button Grid.Column="0" Content="Get 10 students from WCF"
                    Command="{Binding Path=GetStudentWCFCommand}"
                    Margin="5,0,0,5"/>
            <Button Grid.Column="1" Content="Get 10 students from wrong method in WCF"
                    Command="{Binding Path=GetStudentWCFWrongMethodCommand}"
                    Margin="5,0,0,5"/>
        </Grid>
        
        <sdk:DataGrid Margin="0, 8, 0, 0" Grid.Row="1"
                      ItemsSource="{Binding Path=StudentList}"/>
    </Grid>
</UserControl>

视图模型具有三个功能的 XAML 组件。数据网格绑定到“StudentList”属性,而两个按钮分别绑定到视图模型中的一个命令。

现在我们完成了对“SilverlightApplication”项目的介绍,接下来准备查看单元测试应用程序“SilverlightApplicationUnitTest”。

Silverlight 测试应用程序“SilverlightApplicationUnitTest”

SolutionExplorerTestApplication.JPG

正如我们前面提到的,Silverlight 应用程序运行在 Web 浏览器的沙盒中。其他类型的应用程序可用的某些功能可能不适用于 Silverlight 应用程序。Silverlight 应用程序的最佳单元测试项目是另一个 Silverlight 应用程序,这样 Silverlight 应用程序和测试应用程序共享相同的运行时环境,我们可以对测试结果更有信心。

Visual Studio 2010 为创建 Silverlight 应用程序的单元测试项目提供了非常好的支持。“SilverlightApplicationUnitTest”是使用 Visual Studio 安装自带的“Silverlight Unit Test Application”项目模板创建的。

设置单元测试应用程序

按照添加新项目到 Visual Studio 解决方案的步骤,我们可以看到一个弹出窗口显示了项目模板的选择。

TestProjectTemplate.JPG

选择“Silverlight Unit Test Application”项目模板,然后按照其余说明操作,就可以轻松地将“SilverlightApplicationUnitTest”添加到 Visual Studio 解决方案中。添加单元测试项目到解决方案后,我们需要进一步设置测试环境。

  • 由于我们将测试“SilverlightApplication”项目中的代码,因此需要从单元测试项目中添加对该项目的引用。
  • 为了测试 WCF 服务调用,我们还需要将 WCF 服务客户端配置文件“ServiceReferences.ClientConfig”添加到单元测试项目中。

为了避免出现两个“ServiceReferences.ClientConfig”文件副本,该文件被添加到了“SilverlightApplicationUnitTest”项目中作为链接文件。您可以在此链接上找到有关如何在 Visual Studio 中添加现有文件作为链接文件的信息。

单元测试的实现

设置好环境后,我们就可以对“SilverlightApplication”项目的视图模型编写一些单元测试了。我们也可以测试应用程序的其他部分,但在本文中,我将测试范围仅限于视图模型。测试是在“Tests.cs”文件中的“MainPageViewModelTests”类中编写的。

using System;
using Microsoft.Silverlight.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SilverlightApplication.ViewModels;
 
namespace SilverlightApplicationUnitTest
{
    [TestClass]
    public class MainPageViewModelTests: SilverlightTest
    {
        [TestMethod]
        public void ViewModelInitiation()
        {
            MainPageViewModel vm = new MainPageViewModel();
            Assert.IsTrue(vm.StudentList == null, 
                "At view model initiation, StudentList should be null");
            Assert.IsTrue(vm.GetStudentWCFCommand != null,
                "At view model initiation, GetStudentWCFCommand should be initiated");
            Assert.IsTrue(vm.GetStudentWCFWrongMethodCommand != null,
                "At view model initiation, 
		GetStudentWCFWrongMethodCommand should be initiated");
        }
 
        [TestMethod]
        [Asynchronous]
        public void AsynchronousWCFCallWithGetStudentWCFCommand()
        {
            MainPageViewModel vm = new MainPageViewModel();
            bool done = false;
            vm.AsyncCallbackCompleted += (() => done = true);
 
            EnqueueCallback(() => vm.GetStudentWCFCommand.Execute(null));
            EnqueueConditional(() => done);
            EnqueueCallback(() => Assert.IsNotNull(vm.StudentList,
                "No student retrieved from the WCF service"));
            EnqueueCallback(() => Assert.IsTrue(vm.StudentList.Count == 10,
                "The number of the students should be 10."));
 
            EnqueueTestComplete();
        }
 
        [TestMethod]
        [Asynchronous]
        public void AsynchronousWCFCallWithGetStudentWCFWrongMethodCommand()
        {
            MainPageViewModel vm = new MainPageViewModel();
            bool done = false;
            vm.AsyncCallbackCompleted += (() => done = true);
 
            EnqueueCallback(() => vm.GetStudentWCFWrongMethodCommand.Execute(null));
            EnqueueConditional(() => done);
            EnqueueCallback(() => Assert.IsNotNull(vm.StudentList,
                "No student retrieved from the WCF service"));
            EnqueueCallback(() => Assert.IsTrue(vm.StudentList.Count == 10,
                "The number of the students should be 10. 
		Problem is captured by the unit test."));
 
            EnqueueTestComplete();
        }
    }
}

MainPageViewModelTests”类中有三个测试方法。“ViewModelInitiation”方法检查视图模型的构造函数是否成功初始化了其状态,以及视图模型中的命令是否成功连接。“GetStudentsFromWCF”和“GetStudentsFromWrongWCFMethod”方法测试视图模型中的命令是否可以启动 WCF 服务调用,以及在异步回调函数中是否从 WCF 服务获得了正确数量的学生。

由于 WCF 服务调用是异步的,因此我们需要使用Jeff Wilcox介绍的机制,以确保在检查 WCF 服务的结果之前始终等待回调函数完成。我们首先初始化一个布尔变量“done”为false,并通过匿名函数订阅视图模型上的“AsyncCallbackCompleted”事件,将变量“done”设置为true。然后,我们使用“EnqueueCallback”方法来发出启动 WCF 服务调用的命令。如果您回到“SilverlightApplication”项目的视图模型中查看回调函数的实现,您会发现“AsyncCallbackCompleted”是在回调函数结束时触发的。“AsyncCallbackCompleted”的触发会促使测试方法中的匿名事件处理程序将变量“done”设置为true。此时,我们确信视图模型中的回调函数已完成执行,并且回调结果已准备好进行检查。为了使此机制生效,您需要在“EnqueueCallback”方法中添加启动 WCF 调用和检查 WCF 服务结果的函数后,调用“EnqueueTestComplete”方法。您可以从 Jeff Wilcox 的博客上阅读更多关于此机制的信息。希望您能获得更多细节。

运行测试应用程序

是的,就这么简单。我们现在已经完成了“SilverlightApplication”项目和单元测试项目。要运行单元测试,我们可以将“SilverlightHostWebApplication”项目设置为启动项目,并将“TestPage.aspx”页面设置为启动页。如果要在 Visual Studio 环境中运行单元测试,需要不带调试地启动单元测试应用程序。如果我们调试运行单元测试,并且代码的某个部分未能通过测试,Visual Studio 可能不会完成测试并将测试结果报告给我们,而是会在代码失败处停止。

键入“Ctrl+F5”在不带调试的情况下启动单元测试,并等待单元测试完成后,您将看到以下结果:

TestApplication.JPG

正如预期的那样,在我们的演示应用程序中,三个测试方法中有两个成功,一个失败。

TestApplicationFailDetail.JPG

点击高亮的失败测试项,我们可以看到失败的详细信息,显示从 WCF 服务接收到的学生数量与预期的数量不符。您可能会问,我们只演示了单元测试项目如何捕获应用程序中的逻辑错误。那么运行时错误呢?答案是单元测试项目同样能很好地捕获运行时错误。您可以在“SilverlightApplication”项目中抛出一些人工异常。如果您的测试方法涵盖了抛出异常的代码,那么运行时异常将被捕获并显示在报告中。

结论

在本文开头,我们陈述了 Silverlight 应用程序单元测试面临的两个特殊挑战。现在我们可以得出结论。

  • Silverlight 应用程序的单元测试项目最好是另一个 Silverlight 应用程序,这样它们可以共享相同的运行时环境,测试结果也更具说服力。我们可以使用 Visual Studio 中的“Silverlight Unit Test Application”项目模板轻松创建此类单元测试项目。
  • 为了解决异步服务调用带来的困难,我们可以使用 Jeff Wilcox 介绍的方法。本文中的单元测试项目演示了如何使用此方法。“SilverlightApplication”项目中的“ViewModelBase”类中引入的公共事件有助于我们在异步回调函数完成时通知测试方法。

关注点

  • 本文提供了一个关于使用异步服务回调对 Silverlight 应用程序进行单元测试的示例。
  • 为了演示如何对 Silverlight 应用程序进行单元测试,我创建了一个简单的 Silverlight MVVM 应用程序。确实,MVVM 模式提高了可测试性,但这并不意味着我们无法对非 MVVM 模式的 Silverlight 应用程序进行单元测试。
  • 本文通过介绍要测试的 Silverlight 应用程序,篇幅变得相当长。我希望您不会因为我冗长的介绍而感到厌烦。这也意味着 MVVM 模式可能会为简单的应用程序引入过多的编程开销。如果您的应用程序很简单,您可以考虑不使用 MVVM 模式,而是直接针对您需要实现的功能进行开发。
  • 本文中的示例单元测试项目仅演示了单元测试如何捕获逻辑错误。但单元测试项目同样能很好地捕获运行时错误。如果您下载了示例代码,您可以在应用程序中抛出一些异常。如果测试方法涵盖了抛出异常的代码,那么它们应该会被捕获并显示在测试报告中。
  • 根据 Jeff Wilcox 的说法,本文中用于单元测试异步回调函数的方法可能仅适用于 Silverlight 应用程序。不能保证它在其他类型的应用程序上也能正常工作。
  • 为了简单起见,Silverlight 应用程序和单元测试应用程序都托管在同一个 ASP.NET Web 应用程序中。在实际应用中,最好将它们托管在不同的 Web 应用程序中。如果您将它们托管在同一个 Web 应用程序中,在将应用程序部署到生产环境时,需要使测试应用程序无法访问。
  • 希望您喜欢我的帖子,也希望这篇文章能以某种方式帮助您。

历史

这是本文的第一个修订版。

© . All rights reserved.