├── .gitignore ├── README.md ├── SampleDotNetCore2RestStub.sln ├── codeCoverage.sh ├── src └── SampleDotNetCore2RestStub │ ├── AppConfig.cs │ ├── Attributes │ └── AuthenticationFilterAttribute.cs │ ├── Controllers │ ├── PersonController.cs │ ├── SecurePersonController.cs │ └── VersionController.cs │ ├── Dockerfile │ ├── Exceptions │ └── HttpException.cs │ ├── Middleware │ └── HttpExceptionMiddleware.cs │ ├── Models │ └── Person.cs │ ├── Program.cs │ ├── Repositories │ ├── IPersonRepository.cs │ └── PersonRepository.cs │ ├── SampleDotNetCore2RestStub.csproj │ ├── Startup.cs │ └── appsettings.json ├── test └── SampleDotNetCore2RestStub.Integration.Test │ ├── BaseTest.cs │ ├── Client │ ├── ApiResponse.cs │ └── PersonServiceClient.cs │ ├── Mocks │ ├── PersonRepositoryStub.cs │ ├── StartupMock.cs │ └── StartupStub.cs │ ├── PersonsTest.cs │ └── SampleDotNetCore2RestStub.Integration.Test.csproj └── tools └── tools.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .vs 3 | bin/ 4 | obj/ 5 | pub/ 6 | 7 | # MiniCover 8 | coverage-html/ 9 | coverage-hits.txt 10 | coverage.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SampleDotNetCore2RestStub # 2 | 3 | Code examples for following posts: 4 | 5 | * Build a REST API with .NET Core 2 and run it on Docker Linux container 6 | * .NET Core integration testing and mock dependencies 7 | * Code coverage of .NET Core unit tests with OpenCover 8 | * .NET Core code coverage on Linux with MiniCover 9 | 10 | ## Build ## 11 | * Install .NET Core SDK 12 | * Open cmd.exe and navigate to project's folder. 13 | * Build project with following command 14 | 15 | `dotnet build` 16 | 17 | ## Run ## 18 | Run project with following command 19 | 20 | `dotnet run` 21 | 22 | ## Functionalities ## 23 | 24 | There are several functionalities implemented in the stub. See linked posts for more details how to use them. 25 | 26 | ### Persons ### 27 | 28 | Database where you can add, get or remove persons with JSON showing RESTful web services functionality described in blog post. GET endpoints are: http://localhost:5000/person/all, http://localhost:5000/person/get/{id}, http://localhost:5000/person/remove. POST endpoint is: http://localhost:5000/person/save. 29 | 30 | ### Version ### 31 | 32 | http://localhost:5000/api/version returns a configuration value that is read from external config file. 33 | 34 | ### Secure Persons ### 35 | 36 | http://localhost:5000/secure/person/all with Authorization header 37 | -------------------------------------------------------------------------------- /SampleDotNetCore2RestStub.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E2FD0252-B344-43E4-8C9C-B1C6C64629F9}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleDotNetCore2RestStub", "src\SampleDotNetCore2RestStub\SampleDotNetCore2RestStub.csproj", "{CDC16DE6-84F9-4613-9A65-DE98AD6E6267}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0E4DFCCB-0C7D-440D-A0E9-11F66A628BA2}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleDotNetCore2RestStub.Integration.Test", "test\SampleDotNetCore2RestStub.Integration.Test\SampleDotNetCore2RestStub.Integration.Test.csproj", "{479C0443-B3F0-4D95-9E6F-0FD15BA1C924}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Debug|x64 = Debug|x64 18 | Debug|x86 = Debug|x86 19 | Release|Any CPU = Release|Any CPU 20 | Release|x64 = Release|x64 21 | Release|x86 = Release|x86 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 27 | {CDC16DE6-84F9-4613-9A65-DE98AD6E6267}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {CDC16DE6-84F9-4613-9A65-DE98AD6E6267}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {CDC16DE6-84F9-4613-9A65-DE98AD6E6267}.Debug|x64.ActiveCfg = Debug|x64 30 | {CDC16DE6-84F9-4613-9A65-DE98AD6E6267}.Debug|x64.Build.0 = Debug|x64 31 | {CDC16DE6-84F9-4613-9A65-DE98AD6E6267}.Debug|x86.ActiveCfg = Debug|x86 32 | {CDC16DE6-84F9-4613-9A65-DE98AD6E6267}.Debug|x86.Build.0 = Debug|x86 33 | {CDC16DE6-84F9-4613-9A65-DE98AD6E6267}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {CDC16DE6-84F9-4613-9A65-DE98AD6E6267}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {CDC16DE6-84F9-4613-9A65-DE98AD6E6267}.Release|x64.ActiveCfg = Release|x64 36 | {CDC16DE6-84F9-4613-9A65-DE98AD6E6267}.Release|x64.Build.0 = Release|x64 37 | {CDC16DE6-84F9-4613-9A65-DE98AD6E6267}.Release|x86.ActiveCfg = Release|x86 38 | {CDC16DE6-84F9-4613-9A65-DE98AD6E6267}.Release|x86.Build.0 = Release|x86 39 | {479C0443-B3F0-4D95-9E6F-0FD15BA1C924}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {479C0443-B3F0-4D95-9E6F-0FD15BA1C924}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {479C0443-B3F0-4D95-9E6F-0FD15BA1C924}.Debug|x64.ActiveCfg = Debug|x64 42 | {479C0443-B3F0-4D95-9E6F-0FD15BA1C924}.Debug|x64.Build.0 = Debug|x64 43 | {479C0443-B3F0-4D95-9E6F-0FD15BA1C924}.Debug|x86.ActiveCfg = Debug|x86 44 | {479C0443-B3F0-4D95-9E6F-0FD15BA1C924}.Debug|x86.Build.0 = Debug|x86 45 | {479C0443-B3F0-4D95-9E6F-0FD15BA1C924}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {479C0443-B3F0-4D95-9E6F-0FD15BA1C924}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {479C0443-B3F0-4D95-9E6F-0FD15BA1C924}.Release|x64.ActiveCfg = Release|x64 48 | {479C0443-B3F0-4D95-9E6F-0FD15BA1C924}.Release|x64.Build.0 = Release|x64 49 | {479C0443-B3F0-4D95-9E6F-0FD15BA1C924}.Release|x86.ActiveCfg = Release|x86 50 | {479C0443-B3F0-4D95-9E6F-0FD15BA1C924}.Release|x86.Build.0 = Release|x86 51 | EndGlobalSection 52 | GlobalSection(NestedProjects) = preSolution 53 | {CDC16DE6-84F9-4613-9A65-DE98AD6E6267} = {E2FD0252-B344-43E4-8C9C-B1C6C64629F9} 54 | {479C0443-B3F0-4D95-9E6F-0FD15BA1C924} = {0E4DFCCB-0C7D-440D-A0E9-11F66A628BA2} 55 | EndGlobalSection 56 | EndGlobal 57 | -------------------------------------------------------------------------------- /codeCoverage.sh: -------------------------------------------------------------------------------- 1 | if [ ! -z $1 ]; then 2 | if [ $1 -lt 0 ] || [ $1 -gt 100 ]; then 3 | echo "Threshold should be between 0 and 100" 4 | threshold=80 5 | fi 6 | threshold=$1 7 | else 8 | threshold=80 9 | fi 10 | 11 | dotnet restore 12 | dotnet build 13 | 14 | cd tools 15 | dotnet restore 16 | 17 | # Instrument assemblies inside 'test' folder to detect hits for source files inside 'src' folder 18 | dotnet minicover instrument --workdir ../ --assemblies test/**/bin/**/*.dll --sources src/**/*.cs 19 | 20 | # Reset hits count in case minicover was run for this project 21 | dotnet minicover reset 22 | 23 | cd .. 24 | 25 | for project in test/**/*.csproj; do dotnet test --no-build $project; done 26 | 27 | cd tools 28 | 29 | # Uninstrument assemblies, it's important if you're going to publish or deploy build outputs 30 | dotnet minicover uninstrument --workdir ../ 31 | 32 | # Create HTML reports inside folder coverage-html 33 | # This command returns failure if the coverage is lower than the threshold 34 | dotnet minicover htmlreport --workdir ../ --threshold $threshold 35 | 36 | # Print console report 37 | # This command returns failure if the coverage is lower than the threshold 38 | dotnet minicover report --workdir ../ --threshold $threshold 39 | 40 | # Create NCover report 41 | dotnet minicover xmlreport --workdir ../ --threshold $threshold 42 | 43 | cd .. -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/AppConfig.cs: -------------------------------------------------------------------------------- 1 | namespace SampleDotNetCore2RestStub 2 | { 3 | public class AppConfig 4 | { 5 | public string Version { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/Attributes/AuthenticationFilterAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net; 4 | using Microsoft.AspNetCore.Mvc.Filters; 5 | using SampleDotNetCore2RestStub.Exceptions; 6 | 7 | namespace SampleDotNetCore2RestStub.Attributes 8 | { 9 | public class AuthenticationFilterAttribute : ActionFilterAttribute 10 | { 11 | public override void OnActionExecuting(ActionExecutingContext context) 12 | { 13 | string authKey = context.HttpContext.Request.Headers["Authorization"].SingleOrDefault(); 14 | 15 | if (string.IsNullOrWhiteSpace(authKey)) 16 | throw new HttpException(HttpStatusCode.Unauthorized); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/Controllers/PersonController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.AspNetCore.Mvc; 4 | using SampleDotNetCore2RestStub.Models; 5 | using SampleDotNetCore2RestStub.Repositories; 6 | 7 | namespace SampleDotNetCore2RestStub.Controllers 8 | { 9 | public class PersonController : Controller 10 | { 11 | private readonly IPersonRepository _personRepository; 12 | 13 | public PersonController(IPersonRepository personRepository) 14 | { 15 | _personRepository = personRepository; 16 | } 17 | 18 | [HttpGet("person/get/{id}")] 19 | public Person GetPerson(int id) 20 | { 21 | return _personRepository.GetById(id); 22 | } 23 | 24 | [HttpGet("person/remove")] 25 | public string RemovePerson() 26 | { 27 | _personRepository.Remove(); 28 | return "Last person remove. Total count: " + _personRepository.GetCount(); 29 | } 30 | 31 | [HttpGet("person/all")] 32 | public List GetPersons() 33 | { 34 | return _personRepository.GetAll(); 35 | } 36 | 37 | [HttpPost("person/save")] 38 | public string AddPerson([FromBody]Person person) 39 | { 40 | return _personRepository.Save(person); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/Controllers/SecurePersonController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.AspNetCore.Mvc; 3 | using SampleDotNetCore2RestStub.Attributes; 4 | using SampleDotNetCore2RestStub.Models; 5 | using SampleDotNetCore2RestStub.Repositories; 6 | 7 | namespace SampleDotNetCore2RestStub.Controllers 8 | { 9 | [ServiceFilter(typeof(AuthenticationFilterAttribute))] 10 | public class SecurePersonController : Controller 11 | { 12 | private readonly IPersonRepository _personRepository; 13 | 14 | public SecurePersonController(IPersonRepository personRepository) 15 | { 16 | _personRepository = personRepository; 17 | } 18 | 19 | [HttpGet("secure/person/all")] 20 | public List GetPersons() 21 | { 22 | return _personRepository.GetAll(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/Controllers/VersionController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.Options; 3 | 4 | namespace SampleDotNetCore2RestStub.Controllers 5 | { 6 | [Route("api/[controller]")] 7 | public class VersionController : Controller 8 | { 9 | private readonly AppConfig _config; 10 | 11 | public VersionController(IOptions options) 12 | { 13 | _config = options.Value; 14 | } 15 | 16 | [HttpGet] 17 | public string Version() 18 | { 19 | return _config.Version; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM microsoft/dotnet:2.0-sdk 2 | COPY pub/ /root/ 3 | WORKDIR /root/ 4 | ENV ASPNETCORE_URLS="http://*:80" 5 | EXPOSE 80/tcp 6 | ENTRYPOINT ["dotnet", "SampleDotNetCore2RestStub.dll"] -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/Exceptions/HttpException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace SampleDotNetCore2RestStub.Exceptions 5 | { 6 | public class HttpException : Exception 7 | { 8 | public int StatusCode { get; } 9 | 10 | public HttpException(HttpStatusCode httpStatusCode) 11 | : base(httpStatusCode.ToString()) 12 | { 13 | this.StatusCode = (int)httpStatusCode; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/Middleware/HttpExceptionMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Http.Features; 4 | using SampleDotNetCore2RestStub.Exceptions; 5 | 6 | namespace SampleDotNetCore2RestStub.Middleware 7 | { 8 | public class HttpExceptionMiddleware 9 | { 10 | private readonly RequestDelegate _next; 11 | 12 | public HttpExceptionMiddleware(RequestDelegate next) 13 | { 14 | _next = next; 15 | } 16 | 17 | public async Task Invoke(HttpContext context) 18 | { 19 | try 20 | { 21 | await _next.Invoke(context); 22 | } 23 | catch (HttpException httpException) 24 | { 25 | context.Response.StatusCode = httpException.StatusCode; 26 | var feature = context.Features.Get(); 27 | feature.ReasonPhrase = httpException.Message; 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/Models/Person.cs: -------------------------------------------------------------------------------- 1 | namespace SampleDotNetCore2RestStub.Models 2 | { 3 | public class Person 4 | { 5 | public int Id { get; set; } 6 | public string FirstName { get; set; } 7 | public string LastName { get; set; } 8 | public string Email { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore; 2 | using Microsoft.AspNetCore.Hosting; 3 | 4 | namespace SampleDotNetCore2RestStub 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | var webHost = WebHost.CreateDefaultBuilder(args) 11 | .UseStartup() 12 | .Build(); 13 | 14 | webHost.Run(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/Repositories/IPersonRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using SampleDotNetCore2RestStub.Models; 3 | 4 | namespace SampleDotNetCore2RestStub.Repositories 5 | { 6 | public interface IPersonRepository 7 | { 8 | Person GetById(int id); 9 | List GetAll(); 10 | int GetCount(); 11 | void Remove(); 12 | string Save(Person person); 13 | } 14 | } -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/Repositories/PersonRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using SampleDotNetCore2RestStub.Models; 4 | 5 | namespace SampleDotNetCore2RestStub.Repositories 6 | { 7 | public class PersonRepository : IPersonRepository 8 | { 9 | private Dictionary _persons = new Dictionary(); 10 | 11 | public PersonRepository() 12 | { 13 | _persons.Add(1, new Person { Id = 1, FirstName = "FN1", LastName = "LN1", Email = "email1@email.na" }); 14 | _persons.Add(2, new Person { Id = 2, FirstName = "FN2", LastName = "LN2", Email = "email2@email.na" }); 15 | _persons.Add(3, new Person { Id = 3, FirstName = "FN3", LastName = "LN3", Email = "email3@email.na" }); 16 | _persons.Add(4, new Person { Id = 4, FirstName = "FN4", LastName = "LN4", Email = "email4@email.na" }); 17 | } 18 | 19 | public Person GetById(int id) 20 | { 21 | return _persons[id]; 22 | } 23 | 24 | public List GetAll() 25 | { 26 | return _persons.Values.ToList(); 27 | } 28 | 29 | public int GetCount() 30 | { 31 | return _persons.Count(); 32 | } 33 | 34 | public void Remove() 35 | { 36 | if (_persons.Keys.Any()) 37 | { 38 | _persons.Remove(_persons.Keys.Last()); 39 | } 40 | } 41 | 42 | public string Save(Person person) 43 | { 44 | if (_persons.ContainsKey(person.Id)) 45 | { 46 | _persons[person.Id] = person; 47 | return "Updated Person with id=" + person.Id; 48 | } 49 | else 50 | { 51 | _persons.Add(person.Id, person); 52 | return "Added Person with id=" + person.Id; 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/SampleDotNetCore2RestStub.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Builder; 5 | using SampleDotNetCore2RestStub.Attributes; 6 | using SampleDotNetCore2RestStub.Middleware; 7 | using SampleDotNetCore2RestStub.Repositories; 8 | 9 | namespace SampleDotNetCore2RestStub 10 | { 11 | public class Startup 12 | { 13 | public Startup() 14 | { 15 | var configurationBuilder = new ConfigurationBuilder() 16 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 17 | .AddEnvironmentVariables(); 18 | 19 | Configuration = configurationBuilder.Build(); 20 | } 21 | 22 | public IConfiguration Configuration { get; } 23 | 24 | public void ConfigureServices(IServiceCollection services) 25 | { 26 | services.AddMvc(); 27 | services.Configure(Configuration); 28 | services.AddScoped(); 29 | 30 | ConfigureRepositories(services); 31 | } 32 | 33 | public virtual void ConfigureRepositories(IServiceCollection services) 34 | { 35 | services.AddSingleton(); 36 | } 37 | 38 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 39 | { 40 | app.UseMiddleware(); 41 | app.UseMvc(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/SampleDotNetCore2RestStub/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "1.0" 3 | } -------------------------------------------------------------------------------- /test/SampleDotNetCore2RestStub.Integration.Test/BaseTest.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.TestHost; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Moq; 7 | using SampleDotNetCore2RestStub.Integration.Test.Client; 8 | using SampleDotNetCore2RestStub.Integration.Test.Mocks; 9 | using SampleDotNetCore2RestStub.Repositories; 10 | 11 | namespace SampleDotNetCore2RestStub.Integration.Test 12 | { 13 | public abstract class BaseTest 14 | { 15 | protected PersonServiceClient PersonServiceClient; 16 | protected Mock PersonRepositoryMock; 17 | 18 | public BaseTest() 19 | { 20 | PersonRepositoryMock = new Mock(); 21 | 22 | var server = new TestServer(new WebHostBuilder() 23 | .UseStartup() 24 | .ConfigureServices(services => 25 | { 26 | services.AddSingleton(PersonRepositoryMock.Object); 27 | })); 28 | 29 | var httpClient = server.CreateClient(); 30 | PersonServiceClient = new PersonServiceClient(httpClient); 31 | } 32 | 33 | [TestCleanup] 34 | public void BaseTearDown() 35 | { 36 | PersonRepositoryMock.Reset(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /test/SampleDotNetCore2RestStub.Integration.Test/Client/ApiResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace SampleDotNetCore2RestStub.Integration.Test.Client 4 | { 5 | public class ApiResponse 6 | { 7 | public HttpStatusCode StatusCode { get; set; } 8 | public T Result { get; set; } 9 | public string ResultAsString { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /test/SampleDotNetCore2RestStub.Integration.Test/Client/PersonServiceClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json; 6 | using SampleDotNetCore2RestStub.Models; 7 | 8 | namespace SampleDotNetCore2RestStub.Integration.Test.Client 9 | { 10 | public class PersonServiceClient 11 | { 12 | private readonly HttpClient _httpClient; 13 | 14 | public PersonServiceClient(HttpClient httpClient) 15 | { 16 | _httpClient = httpClient; 17 | } 18 | 19 | public async Task> GetPerson(string id) 20 | { 21 | var person = await GetAsync($"/person/get/{id}"); 22 | return person; 23 | } 24 | 25 | public async Task>> GetPersons() 26 | { 27 | var persons = await GetAsync>("/person/all"); 28 | return persons; 29 | } 30 | 31 | public async Task> Version() 32 | { 33 | var version = await GetAsync("api/version"); 34 | return version; 35 | } 36 | 37 | private async Task> GetAsync(string path) 38 | { 39 | var response = await _httpClient.GetAsync(path); 40 | var value = await response.Content.ReadAsStringAsync(); 41 | var result = new ApiResponse 42 | { 43 | StatusCode = response.StatusCode, 44 | ResultAsString = value 45 | }; 46 | 47 | try 48 | { 49 | result.Result = JsonConvert.DeserializeObject(value); 50 | } 51 | catch (Exception) 52 | { 53 | // Nothing to do 54 | } 55 | 56 | return result; 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /test/SampleDotNetCore2RestStub.Integration.Test/Mocks/PersonRepositoryStub.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using SampleDotNetCore2RestStub.Models; 4 | using SampleDotNetCore2RestStub.Repositories; 5 | 6 | namespace SampleDotNetCore2RestStub.Integration.Test.Mocks 7 | { 8 | public class PersonRepositoryStub : IPersonRepository 9 | { 10 | private Dictionary _persons = new Dictionary(); 11 | 12 | public PersonRepositoryStub() 13 | { 14 | _persons.Add(1, new Person { Id = 1, FirstName = "Stubbed FN1", LastName = "Stubbed LN1", Email = "stubbed.email1@email.na" }); 15 | } 16 | 17 | public Person GetById(int id) 18 | { 19 | return _persons[id]; 20 | } 21 | 22 | public List GetAll() 23 | { 24 | return _persons.Values.ToList(); 25 | } 26 | 27 | public int GetCount() 28 | { 29 | return _persons.Count(); 30 | } 31 | 32 | public void Remove() 33 | { 34 | if (_persons.Keys.Any()) 35 | { 36 | _persons.Remove(_persons.Keys.Last()); 37 | } 38 | } 39 | 40 | public string Save(Person person) 41 | { 42 | var result = ""; 43 | if (_persons.ContainsKey(person.Id)) 44 | { 45 | result = "Updated Person with id=" + person.Id; 46 | } 47 | else 48 | { 49 | result = "Added Person with id=" + person.Id; 50 | } 51 | _persons.Add(person.Id, person); 52 | return result; 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /test/SampleDotNetCore2RestStub.Integration.Test/Mocks/StartupMock.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using SampleDotNetCore2RestStub.Repositories; 3 | 4 | namespace SampleDotNetCore2RestStub.Integration.Test.Mocks 5 | { 6 | public class StartupMock : Startup 7 | { 8 | private IPersonRepository _personRepository; 9 | 10 | public StartupMock(IPersonRepository personRepository) 11 | { 12 | _personRepository = personRepository; 13 | } 14 | 15 | public override void ConfigureRepositories(IServiceCollection services) 16 | { 17 | services.AddSingleton(_personRepository); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /test/SampleDotNetCore2RestStub.Integration.Test/Mocks/StartupStub.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using SampleDotNetCore2RestStub.Repositories; 3 | 4 | namespace SampleDotNetCore2RestStub.Integration.Test.Mocks 5 | { 6 | public class StartupStub : Startup 7 | { 8 | public override void ConfigureRepositories(IServiceCollection services) 9 | { 10 | services.AddSingleton(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /test/SampleDotNetCore2RestStub.Integration.Test/PersonsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.TestHost; 8 | using Microsoft.VisualStudio.TestTools.UnitTesting; 9 | using Moq; 10 | using Newtonsoft.Json; 11 | using SampleDotNetCore2RestStub.Models; 12 | 13 | namespace SampleDotNetCore2RestStub.Integration.Test 14 | { 15 | [TestClass] 16 | public class PersonsTest : BaseTest 17 | { 18 | private readonly Person _person = new Person 19 | { 20 | Id = 1, 21 | FirstName = "Mocked FN1", 22 | LastName = "Mocked LN1", 23 | Email = "mocked.email1@email.na" 24 | }; 25 | 26 | [TestMethod] 27 | public async Task GetPerson_ReturnsCorrectResult() 28 | { 29 | PersonRepositoryMock.Setup(x => x.GetById(It.IsAny())) 30 | .Returns(_person); 31 | 32 | var response = await PersonServiceClient.GetPerson("1"); 33 | 34 | Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 35 | Assert.AreEqual("Mocked LN1", response.Result.LastName); 36 | } 37 | 38 | [TestMethod] 39 | [ExpectedException(typeof(InvalidOperationException))] 40 | public async Task GetPerson_ThrowsException() 41 | { 42 | PersonRepositoryMock.Setup(x => x.GetById(It.IsAny())) 43 | .Throws(new InvalidOperationException()); 44 | 45 | var result = await PersonServiceClient.GetPerson("1"); 46 | } 47 | 48 | [TestMethod] 49 | public async Task GetPersons() 50 | { 51 | PersonRepositoryMock.Setup(x => x.GetAll()) 52 | .Returns(new List { _person }); 53 | 54 | var response = await PersonServiceClient.GetPersons(); 55 | 56 | Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); 57 | Assert.AreEqual(1, response.Result.Count); 58 | Assert.AreEqual("Mocked LN1", response.Result[0].LastName); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /test/SampleDotNetCore2RestStub.Integration.Test/SampleDotNetCore2RestStub.Integration.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tools/tools.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | --------------------------------------------------------------------------------