Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
259 changes: 259 additions & 0 deletions dotnet-testing/AspNetCoreTestingSeries/AspNetCoreTestingSeries.sln

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.11" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\EmployeesApp\EmployeesApp.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;

namespace EmployeesApp.IntegrationTests
{
public class EmployeesControllerIntegrationTests : IClassFixture<TestingWebAppFactory<Program>>
{
private readonly HttpClient _client;

public EmployeesControllerIntegrationTests(TestingWebAppFactory<Program> factory)
=> _client = factory.CreateClient();

[Fact]
public async Task Index_WhenCalled_ReturnsApplicationForm()
{
var response = await _client.GetAsync("/Employees");

response.EnsureSuccessStatusCode();

var responseString = await response.Content.ReadAsStringAsync();

Assert.Contains("Mark", responseString);
Assert.Contains("Evelin", responseString);
}

[Fact]
public async Task Create_WhenCalled_ReturnsCreateForm()
{
var response = await _client.GetAsync("/Employees/Create");

response.EnsureSuccessStatusCode();

var responseString = await response.Content.ReadAsStringAsync();

Assert.Contains("Please provide a new employee data", responseString);
}

[Fact]
public async Task Create_SentWrongModel_ReturnsViewWithErrorMessages()
{
var postRequest = new HttpRequestMessage(HttpMethod.Post, "/Employees/Create");

var formModel = new Dictionary<string, string>
{
{ "Name", "New Employee" },
{ "Age", "25" }
};

postRequest.Content = new FormUrlEncodedContent(formModel);

var response = await _client.SendAsync(postRequest);

response.EnsureSuccessStatusCode();

var responseString = await response.Content.ReadAsStringAsync();

Assert.Contains("Account number is required", responseString);
}

[Fact]
public async Task Create_WhenPOSTExecuted_ReturnsToIndexViewWithCreatedEmployee()
{
var postRequest = new HttpRequestMessage(HttpMethod.Post, "/Employees/Create");

var formModel = new Dictionary<string, string>
{
{ "Name", "New Employee" },
{ "Age", "25" },
{ "AccountNumber", "214-5874986532-21" }
};

postRequest.Content = new FormUrlEncodedContent(formModel);

var response = await _client.SendAsync(postRequest);

response.EnsureSuccessStatusCode();

var responseString = await response.Content.ReadAsStringAsync();

Assert.Contains("New Employee", responseString);
Assert.Contains("214-5874986532-21", responseString);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:53949/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"EmployeesApp.IntegrationTests": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:53950/"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using EmployeesApp.Models;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
using System.Linq;

namespace EmployeesApp.IntegrationTests
{
public class TestingWebAppFactory<TEntryPoint> : WebApplicationFactory<Program> where TEntryPoint : Program
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services.RemoveAll<DbContextOptions<EmployeeContext>>();
services.RemoveAll<IDbContextOptionsConfiguration<EmployeeContext>>();

services.AddDbContext<EmployeeContext>(options =>
{
options.UseInMemoryDatabase("InMemoryEmployeeTest");
});

var sp = services.BuildServiceProvider();
using (var scope = sp.CreateScope())
using (var appContext = scope.ServiceProvider.GetRequiredService<EmployeeContext>())
{
try
{
appContext.Database.EnsureCreated();
}
catch (Exception ex)
{
//Log errors or do anything you think it's needed
throw;
}
}
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using EmployeesApp.Contracts;
using EmployeesApp.Controllers;
using EmployeesApp.Models;
using Microsoft.AspNetCore.Mvc;
using Moq;
using System.Collections.Generic;
using Xunit;

namespace EmployeesApp.Tests.Controller
{
public class EmployeesControllerTests
{
private readonly Mock<IEmployeeRepository> _mockRepo;
private readonly EmployeesController _controller;

public EmployeesControllerTests()
{
_mockRepo = new Mock<IEmployeeRepository>();
_controller = new EmployeesController(_mockRepo.Object);
}

[Fact]
public void Index_ActionExecutes_ReturnsViewForIndex()
{
var result = _controller.Index();
Assert.IsType<ViewResult>(result);
}

[Fact]
public void Index_ActionExecutes_ReturnsExactNumberOfEmployees()
{
_mockRepo.Setup(repo => repo.GetAll())
.Returns(new List<Employee>() { new Employee(), new Employee() });

var result = _controller.Index();

var viewResult = Assert.IsType<ViewResult>(result);
var employees = Assert.IsType<List<Employee>>(viewResult.Model);
Assert.Equal(2, employees.Count);
}

[Fact]
public void Create_ActionExecutes_ReturnsViewForCreate()
{
var result = _controller.Create();

Assert.IsType<ViewResult>(result);
}

[Fact]
public void Create_InvalidModelState_ReturnsView()
{
_controller.ModelState.AddModelError("Name", "Name is required");

var employee = new Employee { Age = 25, AccountNumber = "255-8547963214-41" };

var result = _controller.Create(employee);

var viewResult = Assert.IsType<ViewResult>(result);
var testEmployee = Assert.IsType<Employee>(viewResult.Model);

Assert.Equal(employee.AccountNumber, testEmployee.AccountNumber);
Assert.Equal(employee.Age, testEmployee.Age);
}

[Fact]
public void Create_InvalidModelState_CreateEmployeeNeverExecutes()
{
_controller.ModelState.AddModelError("Name", "Name is required");

var employee = new Employee { Age = 34 };

_controller.Create(employee);

_mockRepo.Verify(x => x.CreateEmployee(It.IsAny<Employee>()), Times.Never);
}

[Fact]
public void Create_ModelStateValid_CreateEmployeeCalledOnce()
{
Employee? emp = null;

_mockRepo.Setup(r => r.CreateEmployee(It.IsAny<Employee>()))
.Callback<Employee>(x => emp = x);

var employee = new Employee
{
Name = "Test Employee",
Age = 32,
AccountNumber = "123-5435789603-21"
};

_controller.Create(employee);
_mockRepo.Verify(x => x.CreateEmployee(It.IsAny<Employee>()), Times.Once);

Assert.Equal(emp.Name, employee.Name);
Assert.Equal(emp.Age, employee.Age);
Assert.Equal(emp.AccountNumber, employee.AccountNumber);
}

[Fact]
public void Create_ActionExecuted_RedirectsToIndexAction()
{
var employee = new Employee
{
Name = "Test Employee",
Age = 45,
AccountNumber = "123-4356874310-43"
};

var result = _controller.Create(employee);

var redirectToActionResult = Assert.IsType<RedirectToActionResult>(result);

Assert.Equal("Index", redirectToActionResult.ActionName);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\EmployeesApp\EmployeesApp.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using EmployeesApp.Validation;
using System;
using Xunit;

namespace EmployeesApp.Tests.Validation
{
public class AccountNumberValidationTests
{
private readonly AccountNumberValidation _validation;

public AccountNumberValidationTests() => _validation = new AccountNumberValidation();

[Fact]
public void IsValid_ValidAccountNumber_ReturnsTrue()
=> Assert.True(_validation.IsValid("123-4543234576-23"));

[Theory]
[InlineData("1234-3454565676-23")]
[InlineData("12-3454565676-23")]
public void IsValid_AccountNumberFirstPartWrong_ReturnsFalse(string accountNumber)
=> Assert.False(_validation.IsValid(accountNumber));

[Theory]
[InlineData("123-345456567-23")]
[InlineData("123-345456567633-23")]
public void IsValid_AccountNumberMiddlePartWrong_ReturnsFalse(string accNumber)
=> Assert.False(_validation.IsValid(accNumber));

[Theory]
[InlineData("123-3434545656-2")]
[InlineData("123-3454565676-233")]
public void IsValid_AccountNumberLastPartWrong_ReturnsFalse(string accNumber)
=> Assert.False(_validation.IsValid(accNumber));

[Theory]
[InlineData("123-345456567633=23")]
[InlineData("123+345456567633-23")]
[InlineData("123+345456567633=23")]
public void IsValid_InvalidDelimiters_ThrowsArgumentException(string accNumber)
=> Assert.Throws<ArgumentException>(() => _validation.IsValid(accNumber));
}
}
Loading
Loading