├── dotnet-core-xunit ├── appsettings.json ├── appsettings.Development.json ├── Helpers │ └── MappingProfile.cs ├── Entities │ └── TestDb │ │ ├── Users.cs │ │ ├── DbInitializer.cs │ │ └── TestDbContext.cs ├── Dtos │ └── UserDto.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── dotnet-core-xunit.csproj ├── Services │ └── UserService.cs ├── Controllers │ └── UserController.cs └── Startup.cs ├── Users.sql ├── .gitignore ├── dotnet-core-xunit-test ├── dotnet-core-xunit-test.csproj ├── Mock │ └── Entities │ │ └── TestDbContextMock.cs ├── Theory │ └── UserTheoryData.cs ├── Fixture │ ├── ContextFixture.cs │ └── ControllerFixture.cs └── UserControllerTest.cs ├── dotnet-core-xunit.sln └── README.md /dotnet-core-xunit/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /dotnet-core-xunit/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Users.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `corexunit`.`Users` ( 2 | `Id` INT NOT NULL AUTO_INCREMENT, 3 | `Email` VARCHAR(255) NOT NULL, 4 | `Password` VARCHAR(255) NOT NULL, 5 | `FullName` VARCHAR(255) NOT NULL, 6 | `LastLogin` DATETIME NULL, 7 | `CreateDate` DATETIME NOT NULL, 8 | `Status` TINYINT(1) NOT NULL DEFAULT 1, 9 | PRIMARY KEY (`Id`), 10 | UNIQUE INDEX `Id_UNIQUE` (`Id` ASC)); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.*~ 3 | project.lock.json 4 | .DS_Store 5 | *.pyc 6 | nupkg/ 7 | 8 | # Visual Studio Code 9 | .vscode 10 | 11 | # Rider 12 | .idea 13 | 14 | # User-specific files 15 | *.suo 16 | *.user 17 | *.userosscache 18 | *.sln.docstates 19 | 20 | # Build results 21 | [Dd]ebug/ 22 | [Dd]ebugPublic/ 23 | [Rr]elease/ 24 | [Rr]eleases/ 25 | x64/ 26 | x86/ 27 | build/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Oo]ut/ 32 | msbuild.log 33 | msbuild.err 34 | msbuild.wrn 35 | 36 | # Visual Studio 2015 37 | .vs/ -------------------------------------------------------------------------------- /dotnet-core-xunit/Helpers/MappingProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using dotnet_core_xunit.Dtos; 3 | using dotnet_core_xunit.Entities.TestDb; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace dotnet_core_xunit.Helpers 10 | { 11 | public class MappingProfile : Profile 12 | { 13 | public MappingProfile() 14 | { 15 | CreateMap(); 16 | CreateMap(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /dotnet-core-xunit/Entities/TestDb/Users.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace dotnet_core_xunit.Entities.TestDb 5 | { 6 | public partial class Users 7 | { 8 | public int Id { get; set; } 9 | public string Email { get; set; } 10 | public string Password { get; set; } 11 | public string FullName { get; set; } 12 | public DateTime? LastLogin { get; set; } 13 | public DateTime CreateDate { get; set; } 14 | public bool Status { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /dotnet-core-xunit/Dtos/UserDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace dotnet_core_xunit.Dtos 4 | { 5 | public class UserDto 6 | { 7 | public class User 8 | { 9 | public int Id { get; set; } 10 | public string Email { get; set; } 11 | public string Password { get; set; } 12 | public string FullName { get; set; } 13 | public DateTime LastLogin { get; set; } 14 | public DateTime CreateDate { get; set; } 15 | public bool Status { get; set; } 16 | } 17 | 18 | } 19 | } -------------------------------------------------------------------------------- /dotnet-core-xunit/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace dotnet_core_xunit 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /dotnet-core-xunit-test/dotnet-core-xunit-test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | dotnet_core_xunit_test 6 | 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /dotnet-core-xunit-test/Mock/Entities/TestDbContextMock.cs: -------------------------------------------------------------------------------- 1 | using dotnet_core_xunit.Entities.TestDb; 2 | using Microsoft.EntityFrameworkCore; 3 | using System; 4 | 5 | namespace dotnet_core_xunit_test.Mock.Entities 6 | { 7 | public partial class TestDbContextMock : TestDbContext 8 | { 9 | 10 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 11 | { 12 | /** 13 | * At this stage, a copy of the actual database is created as a memory database. 14 | * You do not need to create a separate test database. 15 | */ 16 | if (!optionsBuilder.IsConfigured) 17 | { 18 | optionsBuilder.UseInMemoryDatabase(Guid.NewGuid().ToString()); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dotnet-core-xunit-test/Theory/UserTheoryData.cs: -------------------------------------------------------------------------------- 1 | using dotnet_core_xunit.Dtos; 2 | using System; 3 | using Xunit; 4 | 5 | namespace dotnet_core_xunit_test.Theory 6 | { 7 | public class UserTheoryData: TheoryData 8 | { 9 | public UserTheoryData() 10 | { 11 | /** 12 | * Each item you add to the TheoryData collection will try to pass your unit test's one by one. 13 | */ 14 | 15 | // mock data created by https://barisates.github.io/pretend 16 | Add(new UserDto.User() 17 | { 18 | Email = "yft97l", 19 | CreateDate = DateTime.Now, 20 | FullName = "8s0quo", 21 | Id = 210544, 22 | Password = "d87btl", 23 | LastLogin = DateTime.Now, 24 | Status = true, 25 | }); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /dotnet-core-xunit/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:60126", 8 | "sslPort": 44373 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "api/user", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "dotnet_core_xunit": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "api/user", 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /dotnet-core-xunit/Entities/TestDb/DbInitializer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using static dotnet_core_xunit.Dtos.UserDto; 8 | 9 | namespace dotnet_core_xunit.Entities.TestDb 10 | { 11 | public static class DataSeeder 12 | { 13 | public static void SeedData(this IApplicationBuilder app) 14 | { 15 | using (IServiceScope serviceScope = app.ApplicationServices.CreateScope()) 16 | { 17 | var db = serviceScope.ServiceProvider.GetService(); 18 | 19 | db.Users.AddRange(new Users[] 20 | { 21 | new Users() {Id = 1, FullName = "Barış Ateş", Email = "info@barisates.com", Password = "123", CreateDate = DateTime.Now, LastLogin = DateTime.Now, Status = true }, 22 | new Users() {Id = 2, FullName = "Kemal Akçıl", Email = "me@kemalakcil.com", Password = "123", CreateDate = DateTime.Now, LastLogin = DateTime.Now, Status = true }, 23 | }); 24 | 25 | db.SaveChanges(); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /dotnet-core-xunit/dotnet-core-xunit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | dotnet_core_xunit 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /dotnet-core-xunit/Entities/TestDb/TestDbContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Metadata; 4 | 5 | namespace dotnet_core_xunit.Entities.TestDb 6 | { 7 | public partial class TestDbContext : DbContext 8 | { 9 | 10 | public virtual DbSet Users { get; set; } 11 | 12 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 13 | { 14 | if (!optionsBuilder.IsConfigured) 15 | { 16 | optionsBuilder.UseInMemoryDatabase("SampleMemoryDatabase"); 17 | } 18 | } 19 | 20 | protected override void OnModelCreating(ModelBuilder modelBuilder) 21 | { 22 | modelBuilder.Entity(entity => 23 | { 24 | entity.Property(e => e.Id).HasColumnName("ID"); 25 | 26 | entity.Property(e => e.CreateDate) 27 | .HasColumnType("datetime") 28 | .HasDefaultValueSql("(getdate())"); 29 | 30 | entity.Property(e => e.Email).IsRequired(); 31 | 32 | entity.Property(e => e.FullName).IsRequired(); 33 | 34 | entity.Property(e => e.Password).IsRequired(); 35 | 36 | entity.Property(e => e.LastLogin) 37 | .HasColumnType("datetime") 38 | .HasDefaultValueSql("(getdate())"); 39 | 40 | entity.Property(e => e.Status) 41 | .IsRequired() 42 | .HasDefaultValueSql("((1))"); 43 | }); 44 | 45 | base.OnModelCreating(modelBuilder); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /dotnet-core-xunit.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.421 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-core-xunit", "dotnet-core-xunit\dotnet-core-xunit.csproj", "{2E3BB7AC-81E2-4003-AE9D-9BB7569F7D52}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet-core-xunit-test", "dotnet-core-xunit-test\dotnet-core-xunit-test.csproj", "{9B9C6FA2-76F7-48F0-881D-DA93A83FA8CE}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {2E3BB7AC-81E2-4003-AE9D-9BB7569F7D52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {2E3BB7AC-81E2-4003-AE9D-9BB7569F7D52}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {2E3BB7AC-81E2-4003-AE9D-9BB7569F7D52}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {2E3BB7AC-81E2-4003-AE9D-9BB7569F7D52}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {9B9C6FA2-76F7-48F0-881D-DA93A83FA8CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {9B9C6FA2-76F7-48F0-881D-DA93A83FA8CE}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {9B9C6FA2-76F7-48F0-881D-DA93A83FA8CE}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {9B9C6FA2-76F7-48F0-881D-DA93A83FA8CE}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {87144579-1E16-4446-97C4-24A6394D8A92} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /dotnet-core-xunit/Services/UserService.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using dotnet_core_xunit.Dtos; 3 | using dotnet_core_xunit.Entities.TestDb; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace dotnet_core_xunit.Services 10 | { 11 | public interface IUserService 12 | { 13 | UserDto.User GetUser(int id); 14 | 15 | UserDto.User AddUser(UserDto.User user); 16 | 17 | UserDto.User DeleteUser(int id); 18 | } 19 | 20 | public class UserService : IUserService 21 | { 22 | 23 | private TestDbContext _testDbContext; 24 | 25 | private IMapper _mapper; 26 | 27 | public UserService(TestDbContext testDbContext, IMapper mapper) 28 | { 29 | _testDbContext = testDbContext; 30 | _mapper = mapper; 31 | } 32 | 33 | public UserDto.User AddUser(UserDto.User user) 34 | { 35 | try 36 | { 37 | Users users = _mapper.Map(user); 38 | 39 | _testDbContext.Users.Add(users); 40 | _testDbContext.SaveChanges(); 41 | 42 | return _mapper.Map(users); 43 | } 44 | catch (Exception exp) 45 | { 46 | return null; 47 | } 48 | } 49 | 50 | public UserDto.User DeleteUser(int id) 51 | { 52 | Users users = _testDbContext.Users.Where(w => w.Id == id).FirstOrDefault(); 53 | 54 | if (users == null) 55 | return null; 56 | 57 | _testDbContext.Users.Remove(users); 58 | _testDbContext.SaveChanges(); 59 | 60 | return _mapper.Map(users); 61 | } 62 | 63 | public UserDto.User GetUser(int id) 64 | { 65 | Users users = _testDbContext.Users.Where(w => w.Id == id).FirstOrDefault(); 66 | 67 | return _mapper.Map(users); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /dotnet-core-xunit/Controllers/UserController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using dotnet_core_xunit.Dtos; 6 | using dotnet_core_xunit.Services; 7 | using Microsoft.AspNetCore.Authorization; 8 | using Microsoft.AspNetCore.Mvc; 9 | using System.IdentityModel.Tokens.Jwt; 10 | using Microsoft.Extensions.Options; 11 | using System.Text; 12 | using Microsoft.IdentityModel.Tokens; 13 | using System.Security.Claims; 14 | 15 | namespace dotnet_core_xunit.Controllers 16 | { 17 | [Route("api/[controller]")] 18 | [ApiController] 19 | public class UserController : ControllerBase 20 | { 21 | private IUserService _userService; 22 | 23 | public UserController(IUserService userService) 24 | { 25 | _userService = userService; 26 | } 27 | 28 | [HttpGet] 29 | public IActionResult Get() 30 | { 31 | return Ok(new string[] { "dotnet-core-xunit-example", $"v{GetType().Assembly.GetName().Version.ToString()}", }); 32 | } 33 | 34 | 35 | [HttpGet("{id}")] 36 | public IActionResult GetUser(int id) 37 | { 38 | UserDto.User user = _userService.GetUser(id); 39 | 40 | if (user == null) 41 | return BadRequest("User not found!"); 42 | 43 | return Ok(user); 44 | } 45 | 46 | 47 | [HttpPost] 48 | public IActionResult AddUser([FromBody]UserDto.User userInfo) 49 | { 50 | UserDto.User user = _userService.AddUser(userInfo); 51 | 52 | if (user == null) 53 | return BadRequest("Failed to add user!"); 54 | 55 | return Ok(user); 56 | } 57 | 58 | 59 | [HttpDelete("{id}")] 60 | public IActionResult Delete(int id) 61 | { 62 | UserDto.User user = _userService.DeleteUser(id); 63 | 64 | if (user == null) 65 | return BadRequest("Failed to delete user!"); 66 | 67 | return Ok(user); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /dotnet-core-xunit/Startup.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using dotnet_core_xunit.Entities.TestDb; 3 | using dotnet_core_xunit.Helpers; 4 | using dotnet_core_xunit.Services; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | 10 | namespace dotnet_core_xunit 11 | { 12 | public class Startup 13 | { 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | public IConfiguration Configuration { get; } 20 | 21 | // This method gets called by the runtime. Use this method to add services to the container. 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | // Auto Mapper Configurations 25 | var mappingConfig = new MapperConfiguration(mc => 26 | { 27 | mc.AddProfile(new MappingProfile()); 28 | }); 29 | 30 | IMapper mapper = mappingConfig.CreateMapper(); 31 | services.AddSingleton(mapper); 32 | services.AddDbContext(); 33 | 34 | services.AddTransient(); 35 | 36 | services.AddControllers(); 37 | 38 | } 39 | 40 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 41 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 42 | { 43 | if (env.IsDevelopment()) 44 | { 45 | app.UseDeveloperExceptionPage(); 46 | } 47 | else 48 | { 49 | app.UseExceptionHandler("/Home/Error"); 50 | } 51 | 52 | app.UseStaticFiles(); 53 | 54 | app.UseCors(x => x 55 | .AllowAnyOrigin() 56 | .AllowAnyMethod() 57 | .AllowAnyHeader()); 58 | 59 | app.UseForwardedHeaders(); 60 | app.UseHsts(); 61 | 62 | app.UseHttpsRedirection(); 63 | 64 | app.UseAuthentication(); 65 | 66 | app.UseRouting(); 67 | 68 | app.UseAuthorization(); 69 | 70 | app.UseEndpoints(endpoints => 71 | { 72 | endpoints.MapControllers(); 73 | }); 74 | 75 | app.SeedData(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /dotnet-core-xunit-test/Fixture/ContextFixture.cs: -------------------------------------------------------------------------------- 1 | using dotnet_core_xunit_test.Mock.Entities; 2 | using System; 3 | 4 | namespace dotnet_core_xunit_test.Fixture 5 | { 6 | public class ContextFixture : IDisposable 7 | { 8 | public TestDbContextMock testDbContextMock; 9 | 10 | public ContextFixture() 11 | { 12 | testDbContextMock = new TestDbContextMock(); 13 | 14 | // mock data created by https://barisates.github.io/pretend 15 | testDbContextMock.Users.AddRange(new dotnet_core_xunit.Entities.TestDb.Users[] 16 | { 17 | // for delete test 18 | new dotnet_core_xunit.Entities.TestDb.Users() 19 | { 20 | Id = 685349, 21 | Email = "0sgtsw", 22 | Password = "jmctby", 23 | FullName = "mq8zp2", 24 | CreateDate = DateTime.Now, 25 | Status = true, 26 | }, 27 | // for get test 28 | new dotnet_core_xunit.Entities.TestDb.Users() 29 | { 30 | Id = 454673, 31 | Email = "0tec4e", 32 | Password = "al9jje", 33 | FullName = "jqvlv2", 34 | CreateDate = DateTime.Now, 35 | Status = true, 36 | } 37 | }); 38 | testDbContextMock.SaveChanges(); 39 | } 40 | 41 | // https://docs.microsoft.com/en-us/visualstudio/code-quality/ca1063?view=vs-2019 42 | #region ImplementIDisposableCorrectly 43 | public void Dispose() 44 | { 45 | Dispose(true); 46 | GC.SuppressFinalize(this); 47 | } 48 | 49 | // NOTE: Leave out the finalizer altogether if this class doesn't 50 | // own unmanaged resources, but leave the other methods 51 | // exactly as they are. 52 | ~ContextFixture() 53 | { 54 | // Finalizer calls Dispose(false) 55 | Dispose(false); 56 | } 57 | 58 | // The bulk of the clean-up code is implemented in Dispose(bool) 59 | protected virtual void Dispose(bool disposing) 60 | { 61 | if (disposing) 62 | { 63 | // free managed resources 64 | if (testDbContextMock != null) 65 | { 66 | testDbContextMock.Dispose(); 67 | testDbContextMock = null; 68 | } 69 | } 70 | } 71 | #endregion 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dotnet-core-xunit-example 2 | **Unit Test in .NET Core Web Api with xUnit.** 3 | 4 | In this example, we have a simple Web Api developed with .Net Core which performs database operations. Our Web API consists of the following endpoints; 5 | 6 | > `UserDto.User GetUser(int id);` get user with id in database 7 | 8 | > `UserDto.User AddUser(UserDto.User user);` add user to database 9 | 10 | > `UserDto.User DeleteUser(int id;` delete user with id from database 11 | 12 | When testing these endpoints, we actually write **functional tests**. In this way, we can manage test processes more realistically. 13 | 14 | Our tests use **memory database** when testing our endpoints and our live data is not affected by the testing process. 15 | 16 | When developing a test, we use the library **[xUnit.net](https://github.com/xunit/xunit "xUnit.net")**. 17 | 18 | ### xUnit 19 | 20 | xUnit is an open-source unit testing tool for the .Net Framework and offers **.NET Core support**. Compared to other unit testing frameworks, it stands out with its ease of development and its approach to behaviors like *SetUp, TearDown, OneTimeSetup*. 21 | 22 | **[Comparing xUnit.net to other frameworks.](https://xunit.net/docs/comparisons.html "Comparing xUnit.net to other frameworks.")** 23 | 24 | **SetUp (before each test)** 25 | XUnit uses constructors for test setup operations. You don't need to use a separate attirubute as in NUnit, MSTest frameworks. 26 | 27 | **TearDown (after each test)** 28 | XUnit uses IDisposable classes for teardown operations. 29 | 30 | **Implementing SetUp and Teardown Method in XUnit;** 31 | 32 | ```csharp 33 | public class TruthTests : IDisposable 34 | { 35 | public TruthTests() 36 | { 37 | // It will work before each test. 38 | // NUnit: [SetUp] 39 | // MSTest: [TestInitialize] 40 | } 41 | 42 | public void Dispose() 43 | { 44 | // It will work after each test. 45 | // NUnit: [TearDown] 46 | // MSTest: [TestCleanup] 47 | } 48 | 49 | [Fact] 50 | public void Test1() 51 | { 52 | // Your Test 53 | } 54 | } 55 | ``` 56 | 57 | **OneTimeSetup (share context between tests)** 58 | 59 | We use xUnit's IClassFixture feature to create shared contexts, such as Databases. With the fixture, we can share a single object instance between all tests. 60 | 61 | For more; [xUnit.net Documentation](https://xunit.net/#documentation "xUnit.net Documentation") 62 | - [Running Tests in Parallel](https://xunit.net/docs/running-tests-in-parallel "Running Tests in Parallel") 63 | - [Shared Context between Tests](https://xunit.net/docs/shared-context "Shared Context between Tests") -------------------------------------------------------------------------------- /dotnet-core-xunit-test/UserControllerTest.cs: -------------------------------------------------------------------------------- 1 | using dotnet_core_xunit.Controllers; 2 | using dotnet_core_xunit.Dtos; 3 | using dotnet_core_xunit_test.Fixture; 4 | using dotnet_core_xunit_test.Theory; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Newtonsoft.Json; 7 | using Xunit; 8 | 9 | namespace dotnet_core_xunit_test 10 | { 11 | // OneTimeSetup 12 | /** https://xunit.net/docs/shared-context */ 13 | public class UserControllerTest : IClassFixture 14 | { 15 | UserController userController; 16 | 17 | /** 18 | * xUnit constructor runs before each test. 19 | */ 20 | public UserControllerTest(ControllerFixture fixture) 21 | { 22 | userController = fixture.userController; 23 | } 24 | 25 | [Fact] 26 | public void Get_WithoutParam_Ok_Test() 27 | { 28 | var result = userController.Get() as OkObjectResult; 29 | 30 | Assert.Equal(200, result.StatusCode); 31 | Assert.True((result.Value as string[]).Length == 2); 32 | } 33 | 34 | [Theory] 35 | [InlineData(0)] 36 | public void GetUser_WithNonUser_ThenBadRequest_Test(int id) 37 | { 38 | var result = userController.GetUser(id) as BadRequestObjectResult; 39 | 40 | Assert.Equal(400, result.StatusCode); 41 | Assert.Equal("User not found!", result.Value); 42 | } 43 | 44 | [Theory] 45 | [InlineData(454673)] 46 | public void GetUser_WithTestData_ThenOk_Test(int id) 47 | { 48 | var result = userController.GetUser(id) as OkObjectResult; 49 | 50 | Assert.Equal(200, result.StatusCode); 51 | Assert.IsType(result.Value); 52 | } 53 | 54 | [Theory] 55 | [ClassData(typeof(UserTheoryData))] 56 | public void AddUser_WithTestData_ThenOk_Test(UserDto.User userInfo) 57 | { 58 | var result = userController.AddUser(userInfo) as OkObjectResult; 59 | 60 | Assert.Equal(200, result.StatusCode); 61 | Assert.Equal(JsonConvert.SerializeObject(userInfo), JsonConvert.SerializeObject(result.Value)); 62 | } 63 | 64 | [Theory] 65 | [InlineData(0)] 66 | public void Delete_WithNonUser_ThenBadRequest_Test(int id) 67 | { 68 | var result = userController.Delete(id) as BadRequestObjectResult; 69 | 70 | Assert.Equal(400, result.StatusCode); 71 | Assert.Equal("Failed to delete user!", result.Value); 72 | } 73 | 74 | [Theory] 75 | [InlineData(685349)] 76 | public void Delete_WithTestData_ThenOk_Test(int id) 77 | { 78 | var result = userController.Delete(id) as OkObjectResult; 79 | 80 | Assert.Equal(200, result.StatusCode); 81 | Assert.IsType(result.Value); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /dotnet-core-xunit-test/Fixture/ControllerFixture.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using dotnet_core_xunit.Controllers; 3 | using dotnet_core_xunit.Helpers; 4 | using dotnet_core_xunit.Services; 5 | using dotnet_core_xunit_test.Mock.Entities; 6 | using System; 7 | 8 | namespace dotnet_core_xunit_test.Fixture 9 | { 10 | public class ControllerFixture : IDisposable 11 | { 12 | 13 | private TestDbContextMock testDbContextMock { get; set; } 14 | private UserService userService { get; set; } 15 | private IMapper mapper { get; set; } 16 | 17 | public UserController userController { get; private set; } 18 | 19 | public ControllerFixture() 20 | { 21 | #region Create mock/memory database 22 | 23 | testDbContextMock = new TestDbContextMock(); 24 | 25 | // mock data created by https://barisates.github.io/pretend 26 | testDbContextMock.Users.AddRange(new dotnet_core_xunit.Entities.TestDb.Users[] 27 | { 28 | // for delete test 29 | new dotnet_core_xunit.Entities.TestDb.Users() 30 | { 31 | Id = 685349, 32 | Email = "0sgtsw", 33 | Password = "jmctby", 34 | FullName = "mq8zp2", 35 | CreateDate = DateTime.Now, 36 | Status = true, 37 | }, 38 | // for get test 39 | new dotnet_core_xunit.Entities.TestDb.Users() 40 | { 41 | Id = 454673, 42 | Email = "0tec4e", 43 | Password = "al9jje", 44 | FullName = "jqvlv2", 45 | CreateDate = DateTime.Now, 46 | Status = true, 47 | } 48 | }); 49 | 50 | testDbContextMock.SaveChanges(); 51 | 52 | #endregion 53 | 54 | #region Mapper settings with original profiles. 55 | 56 | var mappingConfig = new MapperConfiguration(mc => 57 | { 58 | mc.AddProfile(new MappingProfile()); 59 | }); 60 | 61 | mapper = mappingConfig.CreateMapper(); 62 | 63 | #endregion 64 | 65 | // Create UserService with Memory Database 66 | userService = new UserService(testDbContextMock, mapper); 67 | 68 | // Create Controller 69 | userController = new UserController(userService); 70 | 71 | } 72 | 73 | #region ImplementIDisposableCorrectly 74 | /** https://docs.microsoft.com/en-us/visualstudio/code-quality/ca1063?view=vs-2019 */ 75 | public void Dispose() 76 | { 77 | Dispose(true); 78 | GC.SuppressFinalize(this); 79 | } 80 | 81 | // NOTE: Leave out the finalizer altogether if this class doesn't 82 | // own unmanaged resources, but leave the other methods 83 | // exactly as they are. 84 | ~ControllerFixture() 85 | { 86 | // Finalizer calls Dispose(false) 87 | Dispose(false); 88 | } 89 | 90 | // The bulk of the clean-up code is implemented in Dispose(bool) 91 | protected virtual void Dispose(bool disposing) 92 | { 93 | if (disposing) 94 | { 95 | testDbContextMock.Dispose(); 96 | testDbContextMock = null; 97 | 98 | userService = null; 99 | mapper = null; 100 | 101 | userController = null; 102 | } 103 | } 104 | #endregion 105 | } 106 | } 107 | --------------------------------------------------------------------------------