Reputation: 151
I am playing around with the new GRPC Service for .Net Core 3.0 template. I have successfully created the Hello World service. I am now trying to add a test project to this solution.
I can see how to make an in-memory server for integration testing for a regular HTTP Client/Server .Net Core service. But I can find no example or guidance anywhere on the net for when using grpc protodef client/server. The issue appears to be with the CreateClient() method which returns a plain old HTTP client, and not a grpc one.
It would be good if a barebones integration test was included with the project created by the VS 2019 template. The main reason for me to look at Microservices and in particular using grpc was the idea that I can have small blocks that are easily testable with an automated testing framework. But I seem to have fallen at the first hurdle.
[Added]
Below is the simple test class. It uses NUnit. The issue I have is finding a way to run the in-memory TestHost, the same way as I do for a .Net Core API or MVC app to allow integration testing.
namespace Project.Tests
{
public class Tests
{
private Greeter.GreeterClient _client;
private TestServer _server;
[OneTimeSetUp]
public void OneTimeSetup()
{
var server = new TestServer(WebHost.CreateDefaultBuilder()
.UseStartup<TestStartup>()
.UseEnvironment("Development")
.UseUrls("https://localhost:5001"));
}
[SetUp]
public void Setup()
{
var channel = GrpcChannel.ForAddress("https://localhost:5001");
_client = new Greeter.GreeterClient(channel);
}
[Test]
public async Task Test1()
{
var reply = await _client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
Assert.IsTrue(reply.Message == "Hello GreeterClient");
}
}
}
Upvotes: 10
Views: 3598
Reputation: 129
I found a pretty simple solution, this is my test class:
public class TestClass:IClassFixture<WebApplicationFactory<Startup>>
{
readonly WebApplicationFactory<Startup> factory;
//Grpc client class, generated from proto
readonly GrpcServices.IntegrationService.IntegrationServiceClient client;
static GrpcChannel CreateHttpsChannel(string webPath, HttpMessageHandler messageHandler)
{
var handler = new GrpcWebHandler(GrpcWebMode.GrpcWebText, messageHandler);
return GrpcChannel.ForAddress(webPath, new GrpcChannelOptions { HttpClient = new HttpClient(handler) });
}
//Startup is startup class from your web app
public TestClass (WebApplicationFactory<Startup> factory)
{
this.factory = factory;
var handler = this.factory.Server.CreateHandler();
var channel = GrpcConnectionFactory.CreateHttpsChannel(this.factory.Server.BaseAddress.ToString(), handler);
client = new GrpcServices.IntegrationService.IntegrationServiceClient(channel);
}
[Fact]
public void TestMethod ()
{
var response = client.Run(new GrpcServices.ServiceRequest { Name = "Name" }); //set your request paramaters
Assert.IsTrue(string.IsNullOrEmpty(response.ErrorMessage));
}
}
Offcourse you have to install xunit and xunit.runner.visualstudio nuget packages.
Upvotes: 0
Reputation: 7522
.net 5 integration test:
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Net.Client;
using Microsoft.AspNetCore.Mvc.Testing;
using WebApplication.Greeter;
using Xunit;
namespace IntegrationTests.GreeterDemo
{
public class GreeterServiceTest : IClassFixture<WebApplicationFactory<WebApplication.Startup>>, IDisposable
{
static readonly Marshaller<HelloRequest> __Marshaller_HelloRequest = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), HelloRequest.Parser.ParseFrom);
static readonly Marshaller<HelloReply> __Marshaller_HelloReply = Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), HelloReply.Parser.ParseFrom);
private readonly WebApplicationFactory<WebApplication.Startup> _factory;
static Method<HelloRequest, HelloReply> __Method_SayHello = new Method<HelloRequest, HelloReply>(
MethodType.Unary,
"Greeter",
"SayHello",
__Marshaller_HelloRequest,
__Marshaller_HelloReply);
public GreeterServiceTest(WebApplicationFactory<WebApplication.Startup> factory)
{
_factory = factory;
}
[Fact]
public async Task test1Async()
{
var client = _factory.CreateClient();
GrpcChannel channel = GrpcChannel.ForAddress(client.BaseAddress, new GrpcChannelOptions
{
HttpClient = client
});
CallInvoker callInvoker = channel.CreateCallInvoker();
HelloRequest request = new()
{
Name = "Stackoverflow Developer"
};
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken cancellationToken = cts.Token;
CallOptions callOptions = new CallOptions(null, DateTime.MaxValue, cancellationToken);
HelloReply reply = await callInvoker.AsyncUnaryCall<HelloRequest, HelloReply>(__Method_SayHello, null, callOptions, request);
Debug.Print(reply.Message);
}
public void Dispose()
{
_factory.Dispose();
}
}
Upvotes: 1
Reputation: 382
Could you please try to pass an HTTPClient created by test server into GRPC channel?
It works when I using WebApplicationFactory, haven't tested for a test server before.
var channel = GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions
{
HttpClient = _server.CreateClient()
});
var client = new Greeter.GreeterClient(channel);
var response await client.SayHelloAsync(new HelloRequest());
// Assert anything you want
Upvotes: 2