Search code examples
.netgrpcgrpc-c#

.NET Grpc unit test CallContext


I am using the packages Grpc.Core and ProtoBuf.Grpc because I want to use both: Code-First gRPC with gRPC ClientFactory. Now I have an interface of form Task<Response> MyMethod(Request request, CallContext context) with CallContext coming from ProtoBuf.Grpc.

In the implementation of the interface I get the authorized (calling) user like this:

var user = httpContext.User.Claims.Where(x => x.Type == JwtRegisteredClaimNames.Name).FirstOrDefault();

Now I want to unit test my implementation of MyMethod but I do not know how to create the correct context for CallContext with a test user.

How can I test my implementation MyMethod with a predefined user in the CallContext?

Edit:

It seems possible to create and pass an instance of CallContext by calling the constructor

public CallContext(object server, ServerCallContext context)

I can create the ServerCallContext object via TestServerCallContext.Create(), see here. However I do not know what to pass for object server. Does anyone have an example for me?


Solution

  • For unit testing gRPC with authentication and user context the following approach was my solution:

    public static ProtoBuf.Grpc.CallContext TestCallContext(Guid user)
    {
        var serverCallContext = TestServerCallContext.Create(
                                                method: nameof(IGrpcGreetingService.Greeting)
                                            , host: "localhost"
                                            , deadline: DateTime.Now.AddMinutes(30)
                                            , requestHeaders: new Metadata()
                                            , cancellationToken: CancellationToken.None
                                            , peer: "10.0.0.25:5001"
                                            , authContext: null
                                            , contextPropagationToken: null
                                            , writeHeadersFunc: (metadata) => Task.CompletedTask
                                            , writeOptionsGetter: () => new WriteOptions()
                                            , writeOptionsSetter: (writeOptions) => { }
                                            );
    
        var claims = new[] {
                new Claim(JwtRegisteredClaimNames.Sub, "some subject"),
                new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
                new Claim(JwtRegisteredClaimNames.Name, user.ToString())
            };
        var claimsIdentity = new ClaimsIdentity(claims);
    
        var httpContext = new DefaultHttpContext();
        httpContext.User = new ClaimsPrincipal(claimsIdentity);
        serverCallContext.UserState["__HttpContext"] = httpContext;
    
        return new ProtoBuf.Grpc.CallContext("", serverCallContext);
    }
    

    With arguments for TestServerCallContext.Create from here.

    Maybe this can help somebody else save some hours or days :)