A tool for smooth, easy use of Microsoft's web host and in-memory test server
Suppose we have a basic user management API, it can:
- Add a user
- Retrieve user information
This API uses a database with EntityFramework and very simple services.
app.MapPost("api/users",async(CreateUserRequestrequest,IUserServiceservice)=>{varcreatedUserId=awaitservice.CreateAsync(request.Name);returnResults.CreatedAtRoute("GetUser",new{id=createdUserId},newCreateUserResponse(createdUserId));});app.MapGet("api/users/{id:guid}",async(Guidid,IUserServiceservice)=>{varuser=awaitservice.GetAsync(id);returnuserisnull?Results.NotFound():Results.Ok(newGetUserResponse(user.Name));}).WithName("GetUser");We simply want to test user creation route, but it's complicated to initialize data in a local database, to substitute services...
You can simply use EasyTestServer to initialize your test with the program you want.
[Fact]publicasyncTaskShould_ReturnExpectedUserName(){//arrangevartestServer=newServer().Build<Program>();varhttpClient=testServer.CreateClient();// setup user to getvarcreateResponse=awaithttpClient.PostAsJsonAsync("api/users",newCreateUserRequest("jean michel"));varid=(awaitcreateResponse.Content.ReadFromJsonAsync<CreateUserResponse>())!.Id;//actvarresponse=awaithttpClient.GetAsync($"api/users/{id}");//assertresponse.StatusCode.Should().Be(HttpStatusCode.OK);varcontent=awaitresponse.Content.ReadFromJsonAsync<GetUserResponse>();content!.Name.Should().Be("jean michel");}In another case, you want to replace a service in dependency injection with a stub.
publicinterfaceIUserService{Task<Guid>CreateAsync(stringname);Task<User?>GetAsync(Guidid);}publicclassStubService:IUserService{
...publicasyncTask<User?>GetAsync(Guidid){returnawaitTask.FromResult(newUser("jean michel stub"));}}You can simply use '.WithService' to replace a service with your stub :
[Fact]publicasyncTaskShould_ReturnUserNameFromStub_When_WithServiceReplaceServiceByStub(){//arrangevartestServer=newServer().WithService<IUserService>(newStubService()).Build<Program>();varhttpClient=testServer.CreateClient();//actvarresponse=awaithttpClient.GetAsync($"api/users/{Guid.NewGuid()}");//assertresponse.StatusCode.Should().Be(HttpStatusCode.OK);varcontent=awaitresponse.Content.ReadFromJsonAsync<GetUserResponse>();content!.Name.Should().Be("jean michel stub");}And you can do the same to use NSubstitute directly (Thank's to https://github.com/nsubstitute/NSubstitute) :
[Fact]publicasyncTaskShould_ReturnUserNameFromSubstitute_When_WithSubstituteReplaceServiceBySubstitute(){//arrangevartestServer=newServer().WithSubstitute<IUserService>(outvarsubstitute).Build<Program>();substitute.GetAsync(Arg.Any<Guid>()).ReturnsForAnyArgs(newUser("jean michel substitute"));varhttpClient=testServer.CreateClient();//actvarresponse=awaithttpClient.GetAsync($"api/users/{Guid.NewGuid()}");//assertresponse.StatusCode.Should().Be(HttpStatusCode.OK);varcontent=awaitresponse.Content.ReadFromJsonAsync<GetUserResponse>();content!.Name.Should().Be("jean michel substitute");}You can go even further by replacing the local database with an in-memory one. To do this, use the "EasyTestServer.EntityFramework" package.
You can use 'UseDatabase' to replace your database with an in-memory one. You can initialize data for your test with 'WithData'.
[Fact]publicasyncTaskShould_ReturnNameFromUser1_When_UseInMemoryDatabaseIsUsedAndWithDataHasAddedUser1(){//arrangevaruser1=newUser("jean charles");varuser2=newUser("jean paul");vartestServer=newServer().UseDatabase().WithData(user1).WithData(user2).Build<UserContext>().Build<Program>();varhttpClient=testServer.CreateClient();//actvarresponse=awaithttpClient.GetAsync($"api/users/{user1.Id}");//assertresponse.StatusCode.Should().Be(HttpStatusCode.OK);varcontent=awaitresponse.Content.ReadFromJsonAsync<GetUserResponse>();content!.Name.Should().Be("jean charles");}You can add or replace any appsettings value with 'WithSetting'.
[Fact]publicasyncTaskShould_ReturnExpectedSettingValue_When_WithSettingIsUsed(){//arrangeconststringsettingKey="TestSetting";vartestServer=newServer().WithSetting(key:settingKey,value:"Expected").Build<Program>();varhttpClient=testServer.CreateClient();//actvarresponse=awaithttpClient.GetAsync($"api/settings/{settingKey}");//assertresponse.StatusCode.Should().Be(HttpStatusCode.OK);varcontent=awaitresponse.Content.ReadFromJsonAsync<GetSettingResponse>();content!.Value.Should().Be("Expected");}