Events
Mar 31, 11 PM - Apr 2, 11 PM
The ultimate Microsoft Fabric, Power BI, SQL, and AI community-led event. March 31 to April 2, 2025.
Register todayThis browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
This isn't the latest version of this article. For the current release, see the .NET 9 version of this article.
Warning
This version of ASP.NET Core is no longer supported. For more information, see the .NET and .NET Core Support Policy. For the current release, see the .NET 9 version of this article.
Important
This information relates to a pre-release product that may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.
For the current release, see the .NET 9 version of this article.
Testing is an important aspect of building stable and maintainable software. This article discusses how to test ASP.NET Core gRPC services.
There are three common approaches for testing gRPC services:
Microsoft.AspNetCore.TestHost
package. gRPC services are tested by calling them using a gRPC client from a unit testing library.In unit testing, only the gRPC service is involved. Dependencies injected into the service must be mocked. In integration testing, the gRPC service and its auxiliary infrastructure are part of the test. This includes app startup, dependency injection, routing and authentication, and authorization.
To demonstrate service tests, review the following service in the sample app.
View or download sample code (how to download)
The TesterService
returns greetings using gRPC's four method types.
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) });
}
}
}
The preceding gRPC service:
IGreeter
.IGreeter
service using a mock object framework, such as Moq. A mocked object is a fabricated object with a predetermined set of property and method behaviors used for testing. For more information, see Integration tests in ASP.NET Core.A unit test library can directly test gRPC services by calling its methods. Unit tests test a gRPC service in isolation.
[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);
}
The preceding unit test:
IGreeter
using Moq.SayHelloUnary
method with a request message and a ServerCallContext
. All service methods have a ServerCallContext
argument. In this test, the type is provided using the TestServerCallContext.Create()
helper method. This helper method is included in the sample code.IGreeter
.gRPC methods can access a request's HttpContext using the ServerCallContext.GetHttpContext
extension method. To unit test a method that uses HttpContext
, the context must be configured in test setup. If HttpContext isn't configured then GetHttpContext
returns null
.
To configure a HttpContext
during test setup, create a new instance and add it to ServerCallContext.UserState
collection using the __HttpContext
key.
var httpContext = new DefaultHttpContext();
var serverCallContext = TestServerCallContext.Create();
serverCallContext.UserState["__HttpContext"] = httpContext;
Execute service methods with this call context to use the configured HttpContext
instance.
Integration tests evaluate an app's components on a broader level than unit tests. The gRPC app is hosted in TestServer, an in-memory test server from the Microsoft.AspNetCore.TestHost
package.
A unit test library starts the gRPC app and then gRPC services are tested using the gRPC client.
The sample code contains infrastructure to make integration testing possible:
GrpcTestFixture<TStartup>
class configures the ASP.NET Core host and starts the gRPC app in an in-memory test server.IntegrationTestBase
class is the base type that integration tests inherit from. It contains the fixture state and APIs for creating a gRPC client to call the gRPC app.[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);
}
The preceding integration test:
IntegrationTestBase
. This type is included in the sample code.SayHelloUnary
method using the gRPC client.Use ConfigureWebHost
on the fixture to override dependencies. Overriding dependencies is useful when an external dependency is unavailable in the test environment. For example, an app that uses an external payment gateway shouldn't call the external dependency when executing tests. Instead, use a mock gateway for the test.
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);
}
The preceding integration test:
MockedGreeterServiceTests
) constructor:
IGreeter
using Moq.IGreeter
registered with dependency injection using ConfigureWebHost
.SayHelloUnary
method using the gRPC client.IGreeter
instance.ASP.NET Core feedback
ASP.NET Core is an open source project. Select a link to provide feedback:
Events
Mar 31, 11 PM - Apr 2, 11 PM
The ultimate Microsoft Fabric, Power BI, SQL, and AI community-led event. March 31 to April 2, 2025.
Register today