简介
在C#开发中引进Blazor,使开发人员有能力将其开发扩展到浏览器中,而无需依靠React、Vue.js和Angular等传统JavaScript结构。
虽然在传统的JavaScript结构中设置测验比较简单,但Blazor需要将一些工具和包整合在一起,然后了解怎么以及在应用程序中测验什么。
这篇文章将介绍怎么为一个简单的Blazor计数器应用程序设置测验,并将其扩展到包括C#开发人员或许想在Blazor应用程序中测验的简直一切内容。
建立一个测验环境
首先,咱们来设置演示项目。
创立一个新项目
在Visual Studio中,点击 “新建“。
从Web和控制台菜单中,挑选应用程序,然后挑选Blazor服务器应用程序。
鄙人一页中,继续不进行验证,然后设置项目称号和解决方案称号。单击 “创立“。
设置一个测验项目
要设置一个测验项目,从文件菜单的下拉菜单中点击新建解决方案;应该会弹出一个模板窗口。
从左侧边栏的Web 和 控制台组,挑选测验,挑选xUnit测验项目,然后点击下一步。
运用与主项目相同的结构版本,然后点击下一步。
最终,为解决方案和项目设置一个称号,然后点击创立。
一旦完成,你的Visual Studio应该有如下的侧边栏。
将主项目链接到测验项目
为了使测验项目能够引证和运用主项目,咱们必须在测验项目中创立一个链接,这样咱们就能够从主项目中导入和运用组件、类和接口。
在Visual Studio里边,从左面的侧边栏中右击测验解决方案,挑选编辑项目文件,然后在同一组里边增加<ProjectReference Include="../path/to/main-project/main-project.csproj" />
,并增加SDK版本。
设置测验依靠性
装置bUnit
从项目菜单中,点击办理NuGet包,查找bUnit,挑选bUnit和bUnit.core,点击增加包,挑选两个解决方案,然后点击OK。
装置xUnit
这个测验项目被引导为xUnit项目。默许情况下,它自带xUnit包。
装置Moq
Moq是一个断语库,对于测验预期成果是否与回来的成果相匹配很有用。
咱们能够用装置bUnit的相同办法来装置Moq。只需查找并挑选Moq,点击增加包,挑选测验项目,然后点击OK。
用bUnit测验
xUnit是一个测验结构,它供给了一个接口,能够在浏览器之外运转Blazor应用程序,并依然经过代码与输出进行交互。
bUnit是一个接口,咱们能够经过它与Blazor组件互动。bUnit供给的接口使咱们有或许在Blazor组件上触发事情,找到组件上的一些元素,并作出断语。
测验设置
要用bUnit测验Blazor应用程序,测验套件必须在测验项目中的一个类中有一个测验用例功用。
测验用例中的代码应该有以下内容。
-
Arrange
, 设置一个TestContext
(一个用于烘托Blazor组件的虚拟环境)。 -
Act
,将一个组件烘托到测验环境中,触发动作,并提出网络恳求。 -
Assert
,检查事情是否被触发以及是否显示了正确的文本。
作为一个比如,下面的设置说明了上述过程。
using BlazorApp.Pages;
using Bunit;
using Xunit;
namespace BlazorAppTests
{
public class CounterTest
{
[Fact]
public void RendersSuccessfully()
{
using var ctx = new TestContext();
// Render Counter component.
var component = ctx.RenderComponent<Counter>();
// Assert: first, find the parent_name vital element, then verify its content.
Assert.Equal("Click me", component.Find($".btn").TextContent);
}
}
}
从右边的侧边栏,点击测验,然后点击全部运转来运转这个测验。
传递参数给组件
有时,组件需要参数才干正确出现。bUnit 供给了一个接口来处理这个问题。
首先,让咱们修改应用程序解决方案中的counter
组件,使其看起来像下面这样。
@page "/counter/{DefaultCount:int?}"
<h1>Counter</h1>
<p>Current count: <span id="counterVal">@currentCount</span></p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
[Parameter]
public int DefaultCount { get; set; }
protected override void OnParametersSet()
{
if (DefaultCount != 0)
{
currentCount = DefaultCount;
}
}
private void IncrementCount()
{
currentCount++;
}
}
首先,注意到咱们怎么更新了路径,以承受一个DefaultCount
的参数,即一个整数。?
告知Blazor,这个参数是可选的,对组件的运转不是必需的。
接下来,注意到C#代码中的DefaultCount
特点有一个[Parameter]
特点。咱们现已将OnParametersSet
生命周期办法挂起,以便在参数被设置时告诉组件。这保证咱们用它来更新组件currentValue
特点,而不是让组件从零开始计数。
咱们能够在bUnit测验用例顶用以下办法烘托这个组件。
using BlazorApp.Pages;
using Bunit;
using Xunit;
namespace BlazorAppTests
{
public class CounterTest
{
public void RendersSuccessfully()
{
using var ctx = new TestContext();
Action onBtnClickHandler = () => { };
// Render Counter component.
var component = ctx.RenderComponent<Counter>(
parameters =>
parameters
// Add parameters
.Add(c => c.DefaultCount, 10)
.Add(c => c.OnBtnClick, onBtnClickHandler)
);
// Assert: first find the parent_name strong element, then verify its content.
Assert.Equal("Click me", component.Find($".btn").TextContent);
}
}
}
在上面的测验中的第14行,咱们烘托组件,然后传递一个回调给组件,调用(p => );
。
然后,咱们将Add
办法增加到参数(p => p.Add(c => c.DefaultCount, 10);
,以便将该参数设置为10。
咱们能够用相同的办法传递一个事情回调,即p.Add(c => c.onBtnClickHandler, onBtnClickHandler)
。这样,咱们在onBtnClickHandler
动作中完成了计数器的递增,而不是在counter
组件中。
将输入和服务传递给组件
有些组件依靠外部服务来运转,而有些则依靠外部字段。咱们能够经过测验上下文中的Services.AddSingleton
办法,用bUnit来完成这一点。
在演示的计数器应用里边,有一个FetchData.razor
文件,它严重依靠一个WeatherForecastService
服务。让咱们测验在xUnit测验项目中运转这个文件。
在测验项目中创立一个名为FetchDataTest.cs
的新文件,并增加以下内容。
using System;
using BlazorApp.Data;
using BlazorApp.Pages;
using Bunit;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace BlazorAppTests
{
public class FetchDataTest
{
[Fact]
public void RendersSuccessfully()
{
using var ctx = new TestContext();
ctx.Services.AddSingleton<WeatherForecastService>(new WeatherForecastService());
// Render Counter component.
var component = ctx.RenderComponent<FetchData>();
Assert.Equal("Weather forecast", component.Find($"h1").TextContent);
}
}
}
注意咱们是怎么运用AddSingleton
接口来增加一个新的服务到咱们的测验运转器上下文的。而当咱们运转这个测验文件时,咱们应该得到一个成功的成果。
事情
上面,咱们看到了如安在测验用例组件内为一个事情设置回调。让咱们看看如安在组件内的一个元素上触发事情。
计数器测验文件有一个按钮,当点击时,会增加计数器。让咱们测验一下,保证咱们能够点击这个按钮,看到页面上的计数更新。
在测验项目中的CounterTest.cs
文件内,在CounterTest
测验套件类中增加以下测验事例。
[Fact]
public void ButtonClickAndUpdatesCount()
{
// Arrange
using var ctx = new TestContext();
var component = ctx.RenderComponent<Counter>();
// Render
var counterValue = "0";
Assert.Equal(counterValue, component.Find($"#counterVal").TextContent);
counterValue = "1";
var buttonElement = component.Find("button");
buttonElement.Click();
Assert.Equal(counterValue, component.Find($"#counterVal").TextContent);
}
在 “摆放 “部分设置了该组件。像往常相同,在 “Render “部分,咱们首先断语该组件从零开始。
然后,咱们运用测验上下文组件的.Find
接口取得按钮的引证,这时回来元素的引证,它也有一些像Click()
办法的API。
最终,咱们断语组件的值,以承认按钮的点击会做相同的动作。
等候异步的状态更新
请注意,在注入服务后,咱们没有测验是否有任何数据被烘托。就像FetchData.razor
组件相同,有些组件需要时刻才干烘托出正确的数据。
咱们能够经过component.waitForState(fn, duration)
办法来等候异步状态的更新。
[Fact]
public void RendersServiceDataSuccessfully()
{
using var ctx = new TestContext();
ctx.Services.AddSingleton<WeatherForecastService>(new WeatherForecastService());
// Render Counter component.
var component = ctx.RenderComponent<FetchData>();
component.WaitForState(() => component.Find(".date").TextContent == "Date");
Assert.Equal("TABLE", component.Find($".table").NodeName);
}
上面的比如等候异步数据的加载,直到WaitForState
中的匿名函数被调用,该函数测验找到一个具有date
类的元素。一旦找到了,咱们就能够对成果做一些进一步的断语。
验证符号
咱们还能够经过MarkupMatches
bUnit接口办法验证一个组件的符号是否遵从相同的形式。
例如,咱们能够测验索引是否包含有 “Hello, World!”文本内容的h1
。
首先,在测验项目内创立一个新文件,命名为IndexTest.cs
,并增加以下内容。
using System;
using BlazorApp.Pages;
using Bunit;
using Xunit;
namespace BlazorAppTests
{
public class IndexTest
{
[Fact]
public void RendersSuccessfully()
{
using var ctx = new TestContext();
// Act
var component = ctx.RenderComponent<BlazorApp.Pages.Index>();
// Assert
Assert.Equal("Hello, world!", component.Find($"h1").TextContent);
}
}
}
除此以外,咱们还能够经过.Find
(咱们现已在这样做了),和FindAll
,来验证一个组件是否包含一个元素,它能够回来一切与查询相匹配的特征。这些办法采用了相似于CSS的挑选器,这使咱们更简单遍历节点。
嘲弄IJSRuntime
IJSRuntime是一个接口,它使得从.Net代码中与JavaScript交互成为或许。
一些组件或许依靠于它;例如,一个组件能够运用jQuery办法来进行API调用。
假如咱们的项目中有JavaScript函数getPageTitle
,咱们能够模仿该函数的调用,这样在咱们组件的任何地方,其成果将是咱们在测验事例中或许指定的。
using var ctx = new TestContext();
ctx.Services.AddSingleton<WeatherForecastService>(new WeatherForecastService());
var theResult = "some result";
ctx.JSInterop.Setup<string>("getPageTitme").SetResult(theResult);
// Render Counter component.
var component = ctx.RenderComponent<FetchData>();
Assert.Equal(theResult, component.Find($".page-title").TextContent);
嘲弄HttpClient
一些应用程序依靠来自长途服务器的数据来正常运转。
单元测验的部分战略是使每个测验用例的依靠性不受影响。而依靠HTTP客户端接触到长途服务器的组件来出现一个功用,假如成果不是静态的,就会破坏咱们的测验。
咱们能够经过模仿HTTPClient来消除这个问题,HTTPClient是一个能够从Blazor应用内部向外部国际发出HTTP恳求的库。
根据bUnit的文档,bUnit默许不包含这个功用,但咱们能够依靠第三方库来完成这个功用。
首先,将RichardSzalay.MockHttp包增加到测验项目中。
dotnet add package RichardSzalay.MockHttp --version 6.0.0
接下来,在测验项目的根部创立一个名为MockHttpClientBunitHelpers
的文件,并增加以下内容。
using Bunit;
using Microsoft.Extensions.DependencyInjection;
using RichardSzalay.MockHttp;
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
public static class MockHttpClientBunitHelpers
{
public static MockHttpMessageHandler AddMockHttpClient(this TestServiceProvider services)
{
var mockHttpHandler = new MockHttpMessageHandler();
var httpClient = mockHttpHandler.ToHttpClient();
httpClient.BaseAddress = new Uri("http://localhost");
services.AddSingleton<HttpClient>(httpClient);
return mockHttpHandler;
}
public static MockedRequest RespondJson<T>(this MockedRequest request, T content)
{
request.Respond(req =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(JsonSerializer.Serialize(content));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return response;
});
return request;
}
public static MockedRequest RespondJson<T>(this MockedRequest request, Func<T> contentProvider)
{
request.Respond(req =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(JsonSerializer.Serialize(contentProvider()));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return response;
});
return request;
}
}
现在,创立一个新的测验事例,并增加以下内容。
[Fact]
public void FetchResultTest()
{
var serverTime = "1632114204";
using var ctx = new TestContext();
var mock = ctx.Services.AddMockHttpClient();
mock.When("/getTime").RespondJson<string>(serverTime);
// Render Counter component.
var component = ctx.RenderComponent<FetchData>();
Assert.Equal(serverTime, component.Find($".time").TextContent);
}
在这里,咱们声明了一个变量,用来保存咱们对服务器的希望,然后经过一个bUnit辅助办法ctx.Services.AddMockHttpClient
,将模仿的客户端增加到上下文服务中,该办法将寻觅MockHttpClientBunitHelpers
,并将其注入到上下文。
然后,咱们运用模仿的引证来模仿咱们希望从路由中得到的响应。最终,咱们断语咱们组件的一部分具有咱们从模仿恳求回来的值。
总结
在这篇文章中,咱们看到了怎么设置一个Blazor项目并增加另一个xUnit测验项目。咱们还将bUnit作为一个测验结构,并讨论了运用bUnit来测验Blazor组件。
除了xUnit作为一个测验结构外,bUnit还能够在nUnit测验结构中运用相似的概念和API运转。
在这篇文章中,咱们介绍了bUnit的一般用法。高级用法可在bUnit文档网站上找到。
The postTesting in Blazor:完好的教程》首先出现在LogRocket博客上。