- 概述
- 新增功能
- 基础知识
- 测试、调试和疑难解答
- 低版本升级迁移
在 ASP.NET Core razor 页单元测试
作者:Luke Latham
ASP.NET Core 支持 Razor 页应用的单元测试。 数据访问层(DAL)和页面模型的测试有助于确保:
- 在应用程序构建过程中,Razor Pages 应用程序的各个部分将独立工作,并作为一个单元一起工作。
- 类和方法的责任范围有限。
- 在应用程序的行为方式上还存在其他文档。
- 回归是指在自动生成和部署过程中发现的代码更新导致的错误。
本主题假定你基本了解 Razor Pages 应用和单元测试。 如果不熟悉 Razor Pages 应用或测试概念,请参阅以下主题:
示例项目包含两个应用:
应用 | 项目文件夹 | 描述 |
---|---|---|
消息应用 | src/RazorPagesTestSample | 允许用户添加消息、删除一条消息、删除所有消息和分析消息(查找每条消息的平均单词数)。 |
测试应用 | tests/RazorPagesTestSample.Tests | 用于对消息应用的 DAL 和索引页模型进行单元测试。 |
可以使用 IDE 的内置测试功能(如Visual Studio或Visual Studio for Mac)运行测试。 如果使用Visual Studio Code或命令行,请在 " RazorPagesTestSample " 文件夹中的命令提示符处执行以下命令:
dotnet test
消息应用组织
消息应用是 Razor Pages 的消息系统,具有以下特征:
- 应用的 "索引" 页(Pages/索引. cshtml和pages/ node.js)提供了一个 UI 和页面模型方法来控制消息的添加、删除和分析(查找每条消息的平均单词数)。
- 消息由
Message
类(Data/message .cs)描述,具有两个属性:Id
(键)和Text
(message)。 此Text
属性是必需的,并且限制为200个字符。 - 使用实体框架的内存中数据库†来存储消息。
- 应用程序在其数据库上下文类
AppDbContext
(Data/AppDbContext)中包含 DAL。 DAL 方法被标记virtual
为,这允许模拟方法在测试中使用。 - 如果数据库在应用启动时为空,则会用三条消息初始化消息存储。 这些种子消息还在测试中使用。
†EF 主题使用 InMemory 进行测试说明了如何将内存中数据库用于使用 MSTest 进行测试。 本主题使用xUnit测试框架。 不同测试框架中的测试概念和测试实现相似,但并不完全相同。
尽管该示例应用程序不使用存储库模式,并且不是工作单元(UoW)模式的有效示例,但 Razor Pages 支持这些模式的开发模式。 有关详细信息,请参阅设计基础结构持久性层和ASP.NET Core 中的测试控制器逻辑 (示例实现存储库模式)。
测试应用组织
测试应用是 "测试/RazorPagesTestSample " 文件夹中的控制台应用。
测试应用文件夹 | 描述 |
---|---|
UnitTests |
|
公用 | 包含用于为每个 DAL 单元测试创建新数据库上下文选项,以便将数据库重置为每个测试的基线条件的方法。TestDbContextOptions |
数据访问层(DAL)的单元测试
消息应用有一个 DAL,其中包含包含在AppDbContext
类中的四个方法(src/RazorPagesTestSample/Data/AppDbContext)。 每个方法都在测试应用程序中有一个或两个单元测试。
DAL 方法 | 函数 |
---|---|
GetMessagesAsync |
从按Text 属性排序的数据库中获取。 List<Message> |
AddMessageAsync |
将添加Message 到数据库中。 |
DeleteAllMessagesAsync |
删除数据库Message 中的所有条目。 |
DeleteMessageAsync |
Message 删除Id 数据库中的单个。 |
为每个测试创建新DbContextOptions AppDbContext
的时,DAL 的单元测试需要。 为每个测试创建DbContextOptions
的一种方法是DbContextOptionsBuilder使用:
var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>() .UseInMemoryDatabase("InMemoryDb"); using (var db = new AppDbContext(optionsBuilder.Options)) { // Use the db here in the unit test. }
此方法的问题是,每个测试都接收到数据库,并将其保留在上一个测试的任何状态。 尝试编写不相互干扰的原子单元测试时,这可能会出现问题。 若要强制AppDbContext
将新的数据库上下文用于每个测试,请DbContextOptions
提供基于新服务提供程序的实例。 测试应用程序演示如何使用其Utilities
类方法TestDbContextOptions
(test /RazorPagesTestSample/实用工具/实用工具)执行此操作:
public static DbContextOptions<AppDbContext> TestDbContextOptions() { // Create a new service provider to create a new in-memory database. var serviceProvider = new ServiceCollection() .AddEntityFrameworkInMemoryDatabase() .BuildServiceProvider(); // Create a new options instance using an in-memory database and // IServiceProvider that the context should resolve all of its // services from. var builder = new DbContextOptionsBuilder<AppDbContext>() .UseInMemoryDatabase("InMemoryDb") .UseInternalServiceProvider(serviceProvider); return builder.Options; }
在 DAL DbContextOptions
单元测试中使用,允许每个测试以原子方式使用全新的数据库实例运行:
using (var db = new AppDbContext(Utilities.TestDbContextOptions())) { // Use the db here in the unit test. }
DataAccessLayerTest
类(run-unittests/DataAccessLayerTest)中的每个测试方法都遵循类似的 "顺序" 操作-断言模式:
- 按为测试配置了数据库,并定义了预期的结果。
- 意义执行测试。
- 断言断言用于确定测试结果是否成功。
例如,该DeleteMessageAsync
方法负责删除由其Id
(src/RazorPagesTestSample/Data/AppDbContext)标识的单个消息:
public async virtual Task DeleteMessageAsync(int id) { var message = await Messages.FindAsync(id); if (message != null) { Messages.Remove(message); await SaveChangesAsync(); } }
此方法有两个测试。 一个测试检查方法是在数据库中存在消息时删除一条消息。 另一种方法测试如果要删除的消息Id
不存在,数据库不会更改。 此DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound
方法如下所示:
[Fact] public async Task DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound() { using (var db = new AppDbContext(Utilities.TestDbContextOptions())) { // Arrange var seedMessages = AppDbContext.GetSeedingMessages(); await db.AddRangeAsync(seedMessages); await db.SaveChangesAsync(); var recId = 1; var expectedMessages = seedMessages.Where(message => message.Id != recId).ToList(); // Act await db.DeleteMessageAsync(recId); // Assert var actualMessages = await db.Messages.AsNoTracking().ToListAsync(); Assert.Equal( expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), actualMessages.OrderBy(m => m.Id).Select(m => m.Text)); } }
首先,方法执行 "排列" 步骤,在该步骤中执行 Act 步骤。 获取并保存seedMessages
种子设定消息。 播种消息会保存到数据库中。 设置为Id
的1
消息将被设置为删除。 执行方法时,预期的消息应包含除为Id
的1
消息之外的所有消息。 DeleteMessageAsync
expectedMessages
变量表示此预期结果。
// Arrange var seedMessages = AppDbContext.GetSeedingMessages(); await db.AddRangeAsync(seedMessages); await db.SaveChangesAsync(); var recId = 1; var expectedMessages = seedMessages.Where(message => message.Id != recId).ToList();
方法的作用:执行方法,并传入recId
的1
: DeleteMessageAsync
// Act await db.DeleteMessageAsync(recId);
最后,方法Messages
从上下文中获取,并将其expectedMessages
与断言等于二者相等:
// Assert var actualMessages = await db.Messages.AsNoTracking().ToListAsync(); Assert.Equal( expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), actualMessages.OrderBy(m => m.Id).Select(m => m.Text));
为了比较这两个List<Message>
是否相同:
- 消息按
Id
排序。 - 在
Text
属性上比较消息对。
类似的测试方法DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound
会检查尝试删除不存在的消息的结果。 在这种情况下,数据库中的预期消息应该等于执行DeleteMessageAsync
方法后的实际消息。 不应更改数据库的内容:
[Fact] public async Task DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound() { using (var db = new AppDbContext(Utilities.TestDbContextOptions())) { // Arrange var expectedMessages = AppDbContext.GetSeedingMessages(); await db.AddRangeAsync(expectedMessages); await db.SaveChangesAsync(); var recId = 4; // Act try { await db.DeleteMessageAsync(recId); } catch { // recId doesn't exist } // Assert var actualMessages = await db.Messages.AsNoTracking().ToListAsync(); Assert.Equal( expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), actualMessages.OrderBy(m => m.Id).Select(m => m.Text)); } }
页面模型方法的单元测试
另一组单元测试负责页面模型方法的测试。 在 message 应用中,索引页模型IndexModel
位于src/RazorPagesTestSample/Pages/ 类中。
页面模型方法 | 函数 |
---|---|
OnGetAsync |
使用GetMessagesAsync 方法获取来自该 UI 的 DAL 的消息。 |
OnPostAddMessageAsync |
如果ModelState有效,则调用AddMessageAsync 将消息添加到数据库。 |
OnPostDeleteAllMessagesAsync |
调用DeleteAllMessagesAsync 以删除数据库中的所有消息。 |
OnPostDeleteMessageAsync |
执行DeleteMessageAsync 以删除具有指定的Id 消息。 |
OnPostAnalyzeMessagesAsync |
如果数据库中有一条或多条消息,则计算每条消息的平均字数。 |
使用IndexPageTests
类中的七个测试(RazorPagesTestSample/run-unittests/IndexPageTests)测试页模型方法。 这些测试使用熟悉的 "排列方式-法" 模式。 这些测试重点关注:
- 确定在ModelState无效时,方法是否遵循正确的行为。
- 确认方法生成正确IActionResult。
- 检查是否已正确赋值。
这组测试通常模拟 DAL 的方法,以便为执行页面模型方法的 Act 步骤生成所需的数据。 例如, GetMessagesAsync
的AppDbContext
方法是模拟,以生成输出。 当页面模型方法执行此方法时,mock 返回结果。 数据不来自数据库。 这会创建可预测、可靠的测试条件,以便在页面模型测试中使用 DAL。
该OnGetAsync_PopulatesThePageModel_WithAListOfMessages
测试显示GetMessagesAsync
了方法对于页面模型是模拟的:
var mockAppDbContext = new Mock<AppDbContext>(optionsBuilder.Options); var expectedMessages = AppDbContext.GetSeedingMessages(); mockAppDbContext.Setup( db => db.GetMessagesAsync()).Returns(Task.FromResult(expectedMessages)); var pageModel = new IndexModel(mockAppDbContext.Object);
在 Act 步骤中执行GetMessagesAsync
方法时,它将调用页模型的方法。OnGetAsync
单元测试 Act 步骤(test /RazorPagesTestSample/run-unittests/IndexPageTests):
// Act await pageModel.OnGetAsync();
IndexPage
页模型的OnGetAsync
方法(src/RazorPagesTestSample/Pages/ ):
public async Task OnGetAsync() { Messages = await _db.GetMessagesAsync(); }
DAL GetMessagesAsync
中的方法不会返回此方法调用的结果。 此方法的模拟版本返回结果。
在该Assert
步骤中,将从页面actualMessages
模型的Messages
属性中指定实际的消息()。 分配消息时也会执行类型检查。 预期的和实际的消息按其Text
属性进行比较。 测试将断言两个List<Message>
实例包含相同的消息。
// Assert var actualMessages = Assert.IsAssignableFrom<List<Message>>(pageModel.Messages); Assert.Equal( expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), actualMessages.OrderBy(m => m.Id).Select(m => m.Text));
此组中的其他测试DefaultHttpContext创建包含ModelStateDictionary PageContext
ActionContext的页模型对象,以及用于建立、 ViewDataDictionary
和的PageContext
。 它们在执行测试时很有用。 例如,消息ModelState
应用程序与一起AddModelError建立错误,以检查在执行时PageResult OnPostAddMessageAsync
是否返回了有效的:
[Fact] public async Task OnPostAddMessageAsync_ReturnsAPageResult_WhenModelStateIsInvalid() { // Arrange var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>() .UseInMemoryDatabase("InMemoryDb"); var mockAppDbContext = new Mock<AppDbContext>(optionsBuilder.Options); var expectedMessages = AppDbContext.GetSeedingMessages(); mockAppDbContext.Setup(db => db.GetMessagesAsync()).Returns(Task.FromResult(expectedMessages)); var httpContext = new DefaultHttpContext(); var modelState = new ModelStateDictionary(); var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState); var modelMetadataProvider = new EmptyModelMetadataProvider(); var viewData = new ViewDataDictionary(modelMetadataProvider, modelState); var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>()); var pageContext = new PageContext(actionContext) { ViewData = viewData }; var pageModel = new IndexModel(mockAppDbContext.Object) { PageContext = pageContext, TempData = tempData, Url = new UrlHelper(actionContext) }; pageModel.ModelState.AddModelError("Message.Text", "The Text field is required."); // Act var result = await pageModel.OnPostAddMessageAsync(); // Assert Assert.IsType<PageResult>(result); }
其他资源
ASP.NET Core 支持 Razor 页应用的单元测试。 数据访问层(DAL)和页面模型的测试有助于确保:
- 在应用程序构建过程中,Razor Pages 应用程序的各个部分将独立工作,并作为一个单元一起工作。
- 类和方法的责任范围有限。
- 在应用程序的行为方式上还存在其他文档。
- 回归是指在自动生成和部署过程中发现的代码更新导致的错误。
本主题假定你基本了解 Razor Pages 应用和单元测试。 如果不熟悉 Razor Pages 应用或测试概念,请参阅以下主题:
示例项目包含两个应用:
应用 | 项目文件夹 | 描述 |
---|---|---|
消息应用 | src/RazorPagesTestSample | 允许用户添加消息、删除一条消息、删除所有消息和分析消息(查找每条消息的平均单词数)。 |
测试应用 | tests/RazorPagesTestSample.Tests | 用于对消息应用的 DAL 和索引页模型进行单元测试。 |
可以使用 IDE 的内置测试功能(如Visual Studio或Visual Studio for Mac)运行测试。 如果使用Visual Studio Code或命令行,请在 " RazorPagesTestSample " 文件夹中的命令提示符处执行以下命令:
dotnet test
消息应用组织
消息应用是 Razor Pages 的消息系统,具有以下特征:
- 应用的 "索引" 页(Pages/索引. cshtml和pages/ node.js)提供了一个 UI 和页面模型方法来控制消息的添加、删除和分析(查找每条消息的平均单词数)。
- 消息由
Message
类(Data/message .cs)描述,具有两个属性:Id
(键)和Text
(message)。 此Text
属性是必需的,并且限制为200个字符。 - 使用实体框架的内存中数据库†来存储消息。
- 应用程序在其数据库上下文类
AppDbContext
(Data/AppDbContext)中包含 DAL。 DAL 方法被标记virtual
为,这允许模拟方法在测试中使用。 - 如果数据库在应用启动时为空,则会用三条消息初始化消息存储。 这些种子消息还在测试中使用。
†EF 主题使用 InMemory 进行测试说明了如何将内存中数据库用于使用 MSTest 进行测试。 本主题使用xUnit测试框架。 不同测试框架中的测试概念和测试实现相似,但并不完全相同。
尽管该示例应用程序不使用存储库模式,并且不是工作单元(UoW)模式的有效示例,但 Razor Pages 支持这些模式的开发模式。 有关详细信息,请参阅设计基础结构持久性层和ASP.NET Core 中的测试控制器逻辑 (示例实现存储库模式)。
测试应用组织
测试应用是 "测试/RazorPagesTestSample " 文件夹中的控制台应用。
测试应用文件夹 | 描述 |
---|---|
UnitTests |
|
公用 | 包含用于为每个 DAL 单元测试创建新数据库上下文选项,以便将数据库重置为每个测试的基线条件的方法。TestDbContextOptions |
数据访问层(DAL)的单元测试
消息应用有一个 DAL,其中包含包含在AppDbContext
类中的四个方法(src/RazorPagesTestSample/Data/AppDbContext)。 每个方法都在测试应用程序中有一个或两个单元测试。
DAL 方法 | 函数 |
---|---|
GetMessagesAsync |
从按Text 属性排序的数据库中获取。 List<Message> |
AddMessageAsync |
将添加Message 到数据库中。 |
DeleteAllMessagesAsync |
删除数据库Message 中的所有条目。 |
DeleteMessageAsync |
Message 删除Id 数据库中的单个。 |
为每个测试创建新DbContextOptions AppDbContext
的时,DAL 的单元测试需要。 为每个测试创建DbContextOptions
的一种方法是DbContextOptionsBuilder使用:
var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>() .UseInMemoryDatabase("InMemoryDb"); using (var db = new AppDbContext(optionsBuilder.Options)) { // Use the db here in the unit test. }
此方法的问题是,每个测试都接收到数据库,并将其保留在上一个测试的任何状态。 尝试编写不相互干扰的原子单元测试时,这可能会出现问题。 若要强制AppDbContext
将新的数据库上下文用于每个测试,请DbContextOptions
提供基于新服务提供程序的实例。 测试应用程序演示如何使用其Utilities
类方法TestDbContextOptions
(test /RazorPagesTestSample/实用工具/实用工具)执行此操作:
public static DbContextOptions<AppDbContext> TestDbContextOptions() { // Create a new service provider to create a new in-memory database. var serviceProvider = new ServiceCollection() .AddEntityFrameworkInMemoryDatabase() .BuildServiceProvider(); // Create a new options instance using an in-memory database and // IServiceProvider that the context should resolve all of its // services from. var builder = new DbContextOptionsBuilder<AppDbContext>() .UseInMemoryDatabase("InMemoryDb") .UseInternalServiceProvider(serviceProvider); return builder.Options; }
在 DAL DbContextOptions
单元测试中使用,允许每个测试以原子方式使用全新的数据库实例运行:
using (var db = new AppDbContext(Utilities.TestDbContextOptions())) { // Use the db here in the unit test. }
DataAccessLayerTest
类(run-unittests/DataAccessLayerTest)中的每个测试方法都遵循类似的 "顺序" 操作-断言模式:
- 按为测试配置了数据库,并定义了预期的结果。
- 意义执行测试。
- 断言断言用于确定测试结果是否成功。
例如,该DeleteMessageAsync
方法负责删除由其Id
(src/RazorPagesTestSample/Data/AppDbContext)标识的单个消息:
public async virtual Task DeleteMessageAsync(int id) { var message = await Messages.FindAsync(id); if (message != null) { Messages.Remove(message); await SaveChangesAsync(); } }
此方法有两个测试。 一个测试检查方法是在数据库中存在消息时删除一条消息。 另一种方法测试如果要删除的消息Id
不存在,数据库不会更改。 此DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound
方法如下所示:
[Fact] public async Task DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound() { using (var db = new AppDbContext(Utilities.TestDbContextOptions())) { // Arrange var seedMessages = AppDbContext.GetSeedingMessages(); await db.AddRangeAsync(seedMessages); await db.SaveChangesAsync(); var recId = 1; var expectedMessages = seedMessages.Where(message => message.Id != recId).ToList(); // Act await db.DeleteMessageAsync(recId); // Assert var actualMessages = await db.Messages.AsNoTracking().ToListAsync(); Assert.Equal( expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), actualMessages.OrderBy(m => m.Id).Select(m => m.Text)); } }
首先,方法执行 "排列" 步骤,在该步骤中执行 Act 步骤。 获取并保存seedMessages
种子设定消息。 播种消息会保存到数据库中。 设置为Id
的1
消息将被设置为删除。 执行方法时,预期的消息应包含除为Id
的1
消息之外的所有消息。 DeleteMessageAsync
expectedMessages
变量表示此预期结果。
// Arrange var seedMessages = AppDbContext.GetSeedingMessages(); await db.AddRangeAsync(seedMessages); await db.SaveChangesAsync(); var recId = 1; var expectedMessages = seedMessages.Where(message => message.Id != recId).ToList();
方法的作用:执行方法,并传入recId
的1
: DeleteMessageAsync
// Act await db.DeleteMessageAsync(recId);
最后,方法Messages
从上下文中获取,并将其expectedMessages
与断言等于二者相等:
// Assert var actualMessages = await db.Messages.AsNoTracking().ToListAsync(); Assert.Equal( expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), actualMessages.OrderBy(m => m.Id).Select(m => m.Text));
为了比较这两个List<Message>
是否相同:
- 消息按
Id
排序。 - 在
Text
属性上比较消息对。
类似的测试方法DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound
会检查尝试删除不存在的消息的结果。 在这种情况下,数据库中的预期消息应该等于执行DeleteMessageAsync
方法后的实际消息。 不应更改数据库的内容:
[Fact] public async Task DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound() { using (var db = new AppDbContext(Utilities.TestDbContextOptions())) { // Arrange var expectedMessages = AppDbContext.GetSeedingMessages(); await db.AddRangeAsync(expectedMessages); await db.SaveChangesAsync(); var recId = 4; // Act await db.DeleteMessageAsync(recId); // Assert var actualMessages = await db.Messages.AsNoTracking().ToListAsync(); Assert.Equal( expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), actualMessages.OrderBy(m => m.Id).Select(m => m.Text)); } }
页面模型方法的单元测试
另一组单元测试负责页面模型方法的测试。 在 message 应用中,索引页模型IndexModel
位于src/RazorPagesTestSample/Pages/ 类中。
页面模型方法 | 函数 |
---|---|
OnGetAsync |
使用GetMessagesAsync 方法获取来自该 UI 的 DAL 的消息。 |
OnPostAddMessageAsync |
如果ModelState有效,则调用AddMessageAsync 将消息添加到数据库。 |
OnPostDeleteAllMessagesAsync |
调用DeleteAllMessagesAsync 以删除数据库中的所有消息。 |
OnPostDeleteMessageAsync |
执行DeleteMessageAsync 以删除具有指定的Id 消息。 |
OnPostAnalyzeMessagesAsync |
如果数据库中有一条或多条消息,则计算每条消息的平均字数。 |
使用IndexPageTests
类中的七个测试(RazorPagesTestSample/run-unittests/IndexPageTests)测试页模型方法。 这些测试使用熟悉的 "排列方式-法" 模式。 这些测试重点关注:
- 确定在ModelState无效时,方法是否遵循正确的行为。
- 确认方法生成正确IActionResult。
- 检查是否已正确赋值。
这组测试通常模拟 DAL 的方法,以便为执行页面模型方法的 Act 步骤生成所需的数据。 例如, GetMessagesAsync
的AppDbContext
方法是模拟,以生成输出。 当页面模型方法执行此方法时,mock 返回结果。 数据不来自数据库。 这会创建可预测、可靠的测试条件,以便在页面模型测试中使用 DAL。
该OnGetAsync_PopulatesThePageModel_WithAListOfMessages
测试显示GetMessagesAsync
了方法对于页面模型是模拟的:
var mockAppDbContext = new Mock<AppDbContext>(optionsBuilder.Options); var expectedMessages = AppDbContext.GetSeedingMessages(); mockAppDbContext.Setup( db => db.GetMessagesAsync()).Returns(Task.FromResult(expectedMessages)); var pageModel = new IndexModel(mockAppDbContext.Object);
在 Act 步骤中执行GetMessagesAsync
方法时,它将调用页模型的方法。OnGetAsync
单元测试 Act 步骤(test /RazorPagesTestSample/run-unittests/IndexPageTests):
// Act await pageModel.OnGetAsync();
IndexPage
页模型的OnGetAsync
方法(src/RazorPagesTestSample/Pages/ ):
public async Task OnGetAsync() { Messages = await _db.GetMessagesAsync(); }
DAL GetMessagesAsync
中的方法不会返回此方法调用的结果。 此方法的模拟版本返回结果。
在该Assert
步骤中,将从页面actualMessages
模型的Messages
属性中指定实际的消息()。 分配消息时也会执行类型检查。 预期的和实际的消息按其Text
属性进行比较。 测试将断言两个List<Message>
实例包含相同的消息。
// Assert var actualMessages = Assert.IsAssignableFrom<List<Message>>(pageModel.Messages); Assert.Equal( expectedMessages.OrderBy(m => m.Id).Select(m => m.Text), actualMessages.OrderBy(m => m.Id).Select(m => m.Text));
此组中的其他测试DefaultHttpContext创建包含ModelStateDictionary PageContext
ActionContext的页模型对象,以及用于建立、 ViewDataDictionary
和的PageContext
。 它们在执行测试时很有用。 例如,消息ModelState
应用程序与一起AddModelError建立错误,以检查在执行时PageResult OnPostAddMessageAsync
是否返回了有效的:
[Fact] public async Task OnPostAddMessageAsync_ReturnsAPageResult_WhenModelStateIsInvalid() { // Arrange var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>() .UseInMemoryDatabase("InMemoryDb"); var mockAppDbContext = new Mock<AppDbContext>(optionsBuilder.Options); var expectedMessages = AppDbContext.GetSeedingMessages(); mockAppDbContext.Setup(db => db.GetMessagesAsync()).Returns(Task.FromResult(expectedMessages)); var httpContext = new DefaultHttpContext(); var modelState = new ModelStateDictionary(); var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState); var modelMetadataProvider = new EmptyModelMetadataProvider(); var viewData = new ViewDataDictionary(modelMetadataProvider, modelState); var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>()); var pageContext = new PageContext(actionContext) { ViewData = viewData }; var pageModel = new IndexModel(mockAppDbContext.Object) { PageContext = pageContext, TempData = tempData, Url = new UrlHelper(actionContext) }; pageModel.ModelState.AddModelError("Message.Text", "The Text field is required."); // Act var result = await pageModel.OnPostAddMessageAsync(); // Assert Assert.IsType<PageResult>(result); }