Craig
Craig

Reputation: 11

How to unit test .Net 6 code that calls a gRPC code first service

I'm setting up a gRPC client. The scenario is I have a REST API that then calls backend services using gRPC Code First. But I can't figure out how to I can inject a mock gRPC service so I can test the code calling the gRPC service. Ideally, I'd like to setup the gRPC service in program.cs then inject it into the class library code but am open to other solutions. Any ideas?

Note the client is .Net 6 Web API.

Upvotes: 1

Views: 89

Answers (1)

Shimmy Weitzhandler
Shimmy Weitzhandler

Reputation: 104692

You can utilize WebApplicationFactory<TEntryPoint> for that (see docs):

Ideally you'd have the actual implementation of your service use an underlying IRepository etc. which you can setup with Mock.

Shared code:

using ProtoBuf.Grpc;
using System.Runtime.Serialization;
using System.ServiceModel;

namespace GrpcExperiments.Shared;

[DataContract]
public class HelloReply
{
    [DataMember(Order = 1)]
    public string? Message { get; set; }
}

[DataContract]
public class HelloRequest
{
    [DataMember(Order = 1)]
    public string? Name { get; set; }
}

[ServiceContract]
public interface IGreeterService
{
    [OperationContract]
    Task<HelloReply> SayHelloAsync(HelloRequest request);
}

Server code:

using ProtoBuf.Grpc;
using GrpcExperiments.Shared;

public class GreeterService : IGreeterService
{
    public Task<HelloReply> SayHelloAsync(HelloRequest request)
    {
        return Task.FromResult(
            new HelloReply { Message = $"Hello {request.Name}" });
    }
}

Test code:

using Gnc = Grpc.Net.ClientFactory;

[Fact]
public async Task TestGreeterService()
{
    using var webApp = new WebApplicationFactory<Program>();
    
    // optional setup of IRepository or other underlying business-layer
    // GreeterService will then have a constructor that takes the IRepository
    webApp.WithWebHostBuilder(hostBuilder => 
        hostBuilder.ConfigureServices(services =>
        {
            var repositoryMock = new Mock<IRepository>();
            repositoryMock.Setup(x => x.GetById(1)).Returns(...);

            services.AddSingleton(repository.Object);
        }));

    var services = new ServiceCollection();
    services
        .AddCodeFirstGrpcClient<IGreeterService>(clientFactoryOptions =>
        {
            clientFactoryOptions.Address = webApp.Server.BaseAddress;
            clientFactoryOptions.ChannelOptionsActions.Add(option =>
                option.HttpHandler = webApp.Server.CreateHandler());
        });

    using var serviceProvider = services.BuildServiceProvider();
    var factory = serviceProvider.GetRequiredService<Gnc.GrpcClientFactory>();
    var client = factory.CreateClient<IGreeterService>(nameof(IGreeterService));

    var reply = await client.SayHelloAsync(
        new HelloRequest { Name = "GreeterClient" });

    Assert.Equal("Hello GreeterClient", reply.Message);
}

Upvotes: 0

Related Questions