├── .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 |
--------------------------------------------------------------------------------