在 ASP.NET Core 中測試 gRPC 服務
注意
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 如需目前的版本,請參閱 本文的 .NET 9 版本。
測試是建置穩定且可維護軟體的重要層面。 本文討論如何測試 ASP.NET Core gRPC 服務。
測試 gRPC 服務的常見方法有三種:
- 單元測試:直接從單元測試程式庫測試 gRPC 服務。
- 整合測試:gRPC 應用程式裝載於 TestServer 套件,這是來自
Microsoft.AspNetCore.TestHost
套件的記憶體內部測試伺服器。 透過從單元測試程式庫使用 gRPC 用戶端呼叫 gRPC 服務來進行測試。 - 手動測試:使用臨機操作呼叫測試 gRPC 伺服器。 如需如何使用命令列和 UI 工具搭配 gRPC 服務的詳細資訊,請參閱在 ASP.NET Core 中使用 gRPCurl 和 gRPCui 測試 gRPC 服務。
在單元測試中,只涉及 gRPC 服務。 必須模擬插入服務中的相依性。 在整合測試中,gRPC 服務及其輔助基礎結構是測試的一部分。 這包括應用程式啟動、相依性插入、路由和驗證,以及授權。
可測試服務範例
若要示範服務測試,請在範例應用程式中檢閱下列服務。
檢視或下載範例程式碼 \(英文\) (如何下載)
TesterService
會使用 gRPC 的四個方法類型傳回問候語。
public class TesterService : Tester.TesterBase
{
private readonly IGreeter _greeter;
public TesterService(IGreeter greeter)
{
_greeter = greeter;
}
public override Task<HelloReply> SayHelloUnary(HelloRequest request,
ServerCallContext context)
{
var message = _greeter.Greet(request.Name);
return Task.FromResult(new HelloReply { Message = message });
}
public override async Task SayHelloServerStreaming(HelloRequest request,
IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
{
var i = 0;
while (!context.CancellationToken.IsCancellationRequested)
{
var message = _greeter.Greet($"{request.Name} {++i}");
await responseStream.WriteAsync(new HelloReply { Message = message });
await Task.Delay(1000);
}
}
public override async Task<HelloReply> SayHelloClientStreaming(
IAsyncStreamReader<HelloRequest> requestStream, ServerCallContext context)
{
var names = new List<string>();
await foreach (var request in requestStream.ReadAllAsync())
{
names.Add(request.Name);
}
var message = _greeter.Greet(string.Join(", ", names));
return new HelloReply { Message = message };
}
public override async Task SayHelloBidirectionalStreaming(
IAsyncStreamReader<HelloRequest> requestStream,
IServerStreamWriter<HelloReply> responseStream,
ServerCallContext context)
{
await foreach (var request in requestStream.ReadAllAsync())
{
await responseStream.WriteAsync(
new HelloReply { Message = _greeter.Greet(request.Name) });
}
}
}
上述 gRPC 服務:
- 遵循明確相依性準則。
- 預期相依性插入 (DI) 以提供
IGreeter
的執行個體。 - 可以使用模擬物件架構 (例如 Moq) 透過模擬
IGreeter
服務進行測試。 「模擬物件」是製作出來的物件,具有一組用於測試的預定屬性和方法行為。 如需詳細資訊,請參閱 ASP.NET Core 中的整合測試。
單元測試 gRPC 服務
單元測試程式庫可以藉由呼叫其方法,直接測試 gRPC 服務。 單元測試會單獨測試 gRPC 服務。
[Fact]
public async Task SayHelloUnaryTest()
{
// Arrange
var mockGreeter = new Mock<IGreeter>();
mockGreeter.Setup(
m => m.Greet(It.IsAny<string>())).Returns((string s) => $"Hello {s}");
var service = new TesterService(mockGreeter.Object);
// Act
var response = await service.SayHelloUnary(
new HelloRequest { Name = "Joe" }, TestServerCallContext.Create());
// Assert
mockGreeter.Verify(v => v.Greet("Joe"));
Assert.Equal("Hello Joe", response.Message);
}
上述單元測試:
- 使用 Moq 模擬
IGreeter
。 - 使用要求訊息和
ServerCallContext
執行SayHelloUnary
方法。 所有服務方法都有ServerCallContext
引數。 在此測試中,會使用TestServerCallContext.Create()
協助程式方法提供類型。 此協助程式方法包含在程式碼範例中。 - 進行判斷提示:
- 確認要求名稱已傳遞至
IGreeter
。 - 服務會傳回預期的回覆訊息。
- 確認要求名稱已傳遞至
gRPC 方法中的單元測試 HttpContext
gRPC 方法可以使用 ServerCallContext.GetHttpContext
擴充方法存取要求的 HttpContext。 若要對使用 HttpContext
的方法進行單元測試,必須在測試設定中設定內容。 如果未 HttpContext 設定 ,則 GetHttpContext
會傳回 null
。
若要在測試設定期間設定 HttpContext
,請建立新的執行個體,並使用 __HttpContext
索引鍵將其新增至 ServerCallContext.UserState
集合。
var httpContext = new DefaultHttpContext();
var serverCallContext = TestServerCallContext.Create();
serverCallContext.UserState["__HttpContext"] = httpContext;
使用此呼叫內容執行服務方法,以使用已設定的 HttpContext
執行個體。
整合測試 gRPC 服務
整合測試評估應用程式元件的層級比單元測試更廣泛。 gRPC 應用程式裝載於 TestServer,這是來自 Microsoft.AspNetCore.TestHost
套件的記憶體內部測試伺服器。
單元測試程式庫會啟動 gRPC 應用程式,然後使用 gRPC 用戶端測試 gRPC 服務。
範例程式碼包含基礎結構,可讓您進行整合測試:
- 類別
GrpcTestFixture<TStartup>
會設定 ASP.NET Core 主機,並在記憶體內部測試伺服器中啟動 gRPC 應用程式。 - 類別
IntegrationTestBase
是整合測試所繼承的基底類型。 其中包含用來建立 gRPC 用戶端以呼叫 gRPC 應用程式的測試固件狀態和 API。
[Fact]
public async Task SayHelloUnaryTest()
{
// Arrange
var client = new Tester.TesterClient(Channel);
// Act
var response = await client.SayHelloUnaryAsync(new HelloRequest { Name = "Joe" });
// Assert
Assert.Equal("Hello Joe", response.Message);
}
上述整合測試:
- 使用
IntegrationTestBase
所提供的通道建立 gRPC 用戶端。 此類型包含在範例程式碼中。 - 使用 gRPC 用戶端呼叫
SayHelloUnary
方法。 - 判斷提示此服務會傳回預期的回覆訊息。
插入模擬相依性
在測試固件上使用 ConfigureWebHost
來覆寫相依性。 在測試環境中無法使用外部相依性時,覆寫相依性很實用。 例如,使用外部付款閘道的應用程式不應該在執行測試時呼叫外部相依性。 請改用模擬閘道進行測試。
public MockedGreeterServiceTests(GrpcTestFixture<Startup> fixture,
ITestOutputHelper outputHelper) : base(fixture, outputHelper)
{
var mockGreeter = new Mock<IGreeter>();
mockGreeter.Setup(
m => m.Greet(It.IsAny<string>())).Returns((string s) =>
{
if (string.IsNullOrEmpty(s))
{
throw new ArgumentException("Name not provided.");
}
return $"Test {s}";
});
Fixture.ConfigureWebHost(builder =>
{
builder.ConfigureServices(
services => services.AddSingleton(mockGreeter.Object));
});
}
[Fact]
public async Task SayHelloUnaryTest_MockGreeter_Success()
{
// Arrange
var client = new Tester.TesterClient(Channel);
// Act
var response = await client.SayHelloUnaryAsync(
new HelloRequest { Name = "Joe" });
// Assert
Assert.Equal("Test Joe", response.Message);
}
上述整合測試:
- 在測試類別的 (
MockedGreeterServiceTests
) 建構函式中:- 使用 Moq 模擬
IGreeter
。 - 使用
ConfigureWebHost
覆寫以相依性插入註冊的IGreeter
。
- 使用 Moq 模擬
- 使用 gRPC 用戶端呼叫
SayHelloUnary
方法。 - 根據模擬
IGreeter
執行個體判斷提示預期的回覆訊息。