preface

gRPC With its rigorous interface definition , Efficient transmission efficiency , Various calling methods and other advantages , Occupy a place in microservice development .dotnet
core Formal support gRPC It's been a while , Official documents also show how to use gRPC It is explained in detail , But about how to gRPC The server and client of , But there's no description . After checking the official code , Some solutions have been found , Here is the summary , For your reference .

This article focuses on gRPC Unit test of server-side code , Include normal calls , Server side flow , Unit test of calling methods such as client stream , in addition , introduce sqlite Memory database mode of , Test database related operations .

get ready gRPC Server project

use dotnet new grpc Command to create a gRPC Server project .

modify protos/greeter.proto, Add two interface methods :
// Server flow rpc SayHellos (HelloRequest) returns (stream HelloReply); // Client stream rpc
Sum (stream HelloRequest) returns (HelloReply);   stay GreeterService Implementation of adding method in : using
System;using System.Collections.Generic; using System.Linq; using
System.Threading.Tasks;using Grpc.Core; using GrpcTest.Server.Models; using
Microsoft.Extensions.Logging;namespace GrpcTest.Server { public class
GreeterService : Greeter.GreeterBase {private readonly ILogger<GreeterService>
_logger;private readonly ApplicationDbContext _db; public
GreeterService(ILogger<GreeterService> logger, ApplicationDbContext db) {
_logger= logger; _db = db; } public override Task<HelloReply>
SayHello(HelloRequest request, ServerCallContext context) {return
Task.FromResult(new HelloReply { Message = "Hello " + request.Name }); } public
override async Task SayHellos(HelloRequest request, IServerStreamWriter
<HelloReply> responseStream, ServerCallContext context) { foreach (var student
in _db.Students) { if (context.CancellationToken.IsCancellationRequested) break;
var message = student.Name; _logger.LogInformation($"Sending greeting {message}.
"); await responseStream.WriteAsync(new HelloReply { Message = message }); } }
public override async Task<HelloReply> Sum(IAsyncStreamReader<HelloRequest>
requestStream, ServerCallContext context) {var sum = 0; await foreach (var
requestin requestStream.ReadAllAsync()) { if (int.TryParse(request.Name, out var
number)) sum+= number; else throw new ArgumentException(" Parameters must be recognizable numbers "); }
return new HelloReply { Message = $"sum is {sum}" }; } } }
SayHello: Simply return a text message .

SayHellos: Read all data from tables in the database , And use server-side streaming to return .

Sum: Get input data from client stream , And calculate the sum of all data , If the entered text cannot be converted to a number , Throw exception .

unit testing

newly build xunit project , And refer to the gRPC project , The following packages are introduced :
<ItemGroup> <PackageReference Include="Grpc.Core.Testing" Version="2.28.1" /> <
PackageReferenceInclude="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.3"
/> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> <
PackageReferenceInclude="moq" Version="4.14.1" /> <PackageReference Include
="xunit" Version="2.4.0" /> <PackageReference Include
="xunit.runner.visualstudio" Version="2.4.0" /> <PackageReference Include
="coverlet.collector" Version="1.2.0" /> </ItemGroup>
forge Logger
Use the following command to forge service Needed logger: var logger = Mock.Of<ILogger<GreeterService>>();
use sqlite inmemory Of DbContext
public static ApplicationDbContext CreateDbContext(){ var db = new
ApplicationDbContext(new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlite(CreateInMemoryDatabase()).Options); db.Database.EnsureCreated();
return db; } private static DbConnection CreateInMemoryDatabase() { var
connection =new SqliteConnection("Filename=:memory:"); connection.Open(); return
connection; }
a key : Although it's memory mode , Database must also be open Of , And it needs to run EnsureCreated, Otherwise, calling the database function will report that the table cannot be found .

forge ServerCallContext

Use the following code to forge :
public static ServerCallContext CreateTestContext(){ return
TestServerCallContext.Create("fooMethod", null, DateTime.UtcNow.AddHours(1), new
Metadata(), CancellationToken.None,"127.0.0.1", null, null, (metadata) =>
TaskUtils.CompletedTask, ()=> new WriteOptions(), (writeOptions) => { }); }
The specific parameters shall be adjusted according to the actual test needs , For example, when the test client cancels the operation , modify CancellationToken parameter .

Tests for normal calls
[Fact] public void SayHello() { var service = new GreeterService(logger, null);
var request = new HelloRequest{Name="world"}; var response =
service.SayHello(request, scc).Result;var expected = "Hello world"; var actual =
response.Message; Assert.Equal(expected, actual); }
among scc = Forged ServerCallContext, If it is not actually used in the tested method , It can also be passed in directly null.

Test of server-side flow

The method of server-side flow consists of a IServerStreamWriter<HelloReply> Parameters of type , This parameter is used to return the calculation results of the method to the caller one by one , You can create a generic class to implement this interface , Store the written message as a list, For testing .
public class TestServerStreamWriter<T> : IServerStreamWriter<T> { public
WriteOptions WriteOptions {get; set; } public List<T> Responses { get; } = new
List<T>(); public Task WriteAsync(T message) { this.Responses.Add(message);
return Task.CompletedTask; } }
When testing , Insert two records into the database table , Then test and compare , See if the interface method returns two records .
public async Task SayHellos(){ var db = TestTools.CreateDbContext(); var
students =new List<Student>{ new Student{Name="1"}, new Student{Name="2"} };
db.AddRange(students); db.SaveChanges();var service = new
GreeterService(logger, db);var request = new HelloRequest{Name="world"}; var sw
=new TestServerStreamWriter<HelloReply>(); await service.SayHellos(request, sw,
scc);var expected = students.Count; var actual = sw.Responses.Count;
Assert.Equal(expected, actual); }
Test of client stream

Similar to server flow , The client stream method also has a parameter type of IAsyncStreamReader<HelloRequest>, Simply implement a class for testing .

This class passes the data that the client wants to pass in directly through the IEnumable<T> Parameter in , Simulate the client's streaming request for multiple data .
public class TestStreamReader<T> : IAsyncStreamReader<T> { private readonly
IEnumerator<T> _stream; public TestStreamReader(IEnumerable<T> list){ _stream =
list.GetEnumerator(); }public T Current => _stream.Current; public Task<bool>
MoveNext(CancellationToken cancellationToken) {return
Task.FromResult(_stream.MoveNext()); } }
Normal process test code
[Fact] public void Sum_NormalInput_ReturnSum() { var service = new
GreeterService(null, null); var data = new List<HelloRequest>{ new
HelloRequest{Name="1"}, new HelloRequest{Name="2"}, }; var stream = new
TestStreamReader<HelloRequest>(data); var response = service.Sum(stream,
scc).Result;var expected = "sum is 3"; var actual = response.Message;
Assert.Equal(expected, actual); }
Test code for parameter error
[Fact] public void Sum_BadInput_ThrowException() { var service = new
GreeterService(null, null); var data = new List<HelloRequest>{ new
HelloRequest{Name="1"}, new HelloRequest{Name="abc"}, }; var stream = new
TestStreamReader<HelloRequest>(data); Assert.ThrowsAsync<ArgumentException>(
async () => await service.Sum(stream, scc)); }
summary

Above code , Through the right gRPC Service depends on key resources mock Or simple implementation , Achieve the purpose of unit test .

Technology
©2019-2020 Toolsou All rights reserved,
JS How to operate C Language console games , Make bricks use Python Do automated testing (pytest The essence of framework ) First knowledge MySQL Comprehensive review ( dried food )Python+OpenCV Detailed explanation of face recognition technology Baidu , Ali , Tencent's internal position level and salary structure , With job suggestions ! Image explanation of over fitting and under fitting Huawei certification HCIA-AI artificial intelligence New York Youth Project “ Recapture Wall Street ”: Safeguarding the interests of retail investors CSS architecture design