Commit 4ff7b575 authored by Steve Smith's avatar Steve Smith

Merge remote-tracking branch 'origin/master' into sqlite

parents 6667ad6d fd6102e1
[![Build Status](https://dev.azure.com/ardalis/CleanArchitecture/_apis/build/status/ardalis.CleanArchitecture?branchName=master)](https://dev.azure.com/ardalis/CleanArchitecture/_build/latest?definitionId=3&branchName=master)
![Test Status](https://img.shields.io/azure-devops/tests/ardalis/CleanArchitecture/3.svg)
[![Test Coverage](https://img.shields.io/azure-devops/coverage/ardalis/CleanArchitecture/3.svg)](https://dev.azure.com/ardalis/CleanArchitecture/_build?definitionId=3)
# CleanArchitecture
A starting point for Clean Architecture with ASP.NET Core. [Clean Architecture](https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html) is just the latest in a series of names for the same loosely-coupled, dependency-inverted architecture. You will also find it named [hexagonal](http://alistair.cockburn.us/Hexagonal+architecture), [ports-and-adapters](http://www.dossier-andreas.net/software_architecture/ports_and_adapters.html), or [onion architecture](http://jeffreypalermo.com/blog/the-onion-architecture-part-1/).
......@@ -5,6 +9,8 @@ A starting point for Clean Architecture with ASP.NET Core. [Clean Architecture](
## Give a Star! :star:
If you like or are using this project to learn or start your solution, please give it a star. Thanks!
## *Now available as a [project template](https://marketplace.visualstudio.com/items?itemName=GregTrevellick.CleanArchitecture) within Visual Studio.*
## Learn More
- [DotNetRocks Podcast Discussion with Steve "ardalis" Smith](https://player.fm/series/net-rocks/clean-architecture-with-steve-smith)
......
# ASP.NET Core
# Build and test ASP.NET Core projects targeting .NET Core.
# Add steps that run tests, create a NuGet package, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core
trigger:
- master
pool:
vmImage: 'Ubuntu-16.04'
variables:
buildConfiguration: 'Release'
steps:
- script: dotnet build --configuration $(buildConfiguration)
displayName: 'dotnet build $(buildConfiguration)'
- task: DotNetCoreCLI@2
inputs:
command: test
projects: '**/*Tests/*.csproj'
arguments: '--configuration $(BuildConfiguration)'
......@@ -11,7 +11,7 @@
<ItemGroup>
<PackageReference Include="Autofac" Version="4.8.1" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.5" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.4" PrivateAssets="all">
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
......
using Microsoft.AspNetCore.Mvc;
namespace CleanArchitecture.Web.Api
{
[Route("api/[controller]")]
[ApiController]
public abstract class BaseApiController : Controller
{
}
}
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
namespace CleanArchitecture.Web.Api
{
public class MetaController : BaseApiController
{
[HttpGet("/info")]
public ActionResult<string> Info()
{
var assembly = typeof(Startup).Assembly;
var creationDate = System.IO.File.GetCreationTime(assembly.Location);
var version = FileVersionInfo.GetVersionInfo(assembly.Location).ProductVersion;
return Ok($"Version: {version}, Last Updated: {creationDate}");
}
}
}
......@@ -7,9 +7,7 @@ using System.Linq;
namespace CleanArchitecture.Web.Api
{
[Route("api/[controller]")]
[ValidateModel]
public class ToDoItemsController : Controller
public class ToDoItemsController : BaseApiController
{
private readonly IRepository _repository;
......
......@@ -22,7 +22,7 @@
<PackageReference Include="Autofac" Version="4.8.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.3.0" />
<PackageReference Include="Dapper" Version="1.50.5" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.5" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.4" PrivateAssets="all">
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
......
......@@ -27,7 +27,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.3" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.4" PrivateAssets="all">
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="2.1.3" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
......
......@@ -4,10 +4,10 @@ using Xunit;
namespace CleanArchitecture.Tests.Core.Entities
{
public class ToDoItemMarkCompleteShould
public class ToDoItemMarkComplete
{
[Fact]
public void SetIsDoneToTrue()
public void SetsIsDoneToTrue()
{
var item = new ToDoItemBuilder().Build();
......@@ -17,7 +17,7 @@ namespace CleanArchitecture.Tests.Core.Entities
}
[Fact]
public void RaiseToDoItemCompletedEvent()
public void RaisesToDoItemCompletedEvent()
{
var item = new ToDoItemBuilder().Build();
......
using CleanArchitecture.Core.Interfaces;
using CleanArchitecture.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Moq;
namespace CleanArchitecture.Tests.Integration.Data
{
public abstract class BaseEfRepoTestFixture
{
protected AppDbContext _dbContext;
protected static DbContextOptions<AppDbContext> CreateNewContextOptions()
{
// Create a fresh service provider, and therefore a fresh
// InMemory database instance.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Create a new options instance telling the context to use an
// InMemory database and the new service provider.
var builder = new DbContextOptionsBuilder<AppDbContext>();
builder.UseInMemoryDatabase("cleanarchitecture")
.UseInternalServiceProvider(serviceProvider);
return builder.Options;
}
protected EfRepository GetRepository()
{
var options = CreateNewContextOptions();
var mockDispatcher = new Mock<IDomainEventDispatcher>();
_dbContext = new AppDbContext(options, mockDispatcher.Object);
return new EfRepository(_dbContext);
}
}
}
using CleanArchitecture.Core.Entities;
using System.Linq;
using Xunit;
namespace CleanArchitecture.Tests.Integration.Data
{
public class EfRepositoryAdd : BaseEfRepoTestFixture
{
[Fact]
public void AddsItemAndSetsId()
{
var repository = GetRepository();
var item = new ToDoItemBuilder().Build();
repository.Add(item);
var newItem = repository.List<ToDoItem>().FirstOrDefault();
Assert.Equal(item, newItem);
Assert.True(newItem?.Id > 0);
}
}
}
using CleanArchitecture.Core.Entities;
using System;
using Xunit;
namespace CleanArchitecture.Tests.Integration.Data
{
public class EfRepositoryDelete : BaseEfRepoTestFixture
{
[Fact]
public void DeletesItemAfterAddingIt()
{
// add an item
var repository = GetRepository();
var initialTitle = Guid.NewGuid().ToString();
var item = new ToDoItemBuilder().Title(initialTitle).Build();
repository.Add(item);
// delete the item
repository.Delete(item);
// verify it's no longer there
Assert.DoesNotContain(repository.List<ToDoItem>(),
i => i.Title == initialTitle);
}
}
}
using CleanArchitecture.Core.Entities;
using CleanArchitecture.Core.Interfaces;
using CleanArchitecture.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using System;
using System.Linq;
using Xunit;
namespace CleanArchitecture.Tests.Integration.Data
{
public class EfRepositoryShould
public class EfRepositoryUpdate : BaseEfRepoTestFixture
{
private AppDbContext _dbContext;
private static DbContextOptions<AppDbContext> CreateNewContextOptions()
{
// Create a fresh service provider, and therefore a fresh
// InMemory database instance.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Create a new options instance telling the context to use an
// InMemory database and the new service provider.
var builder = new DbContextOptionsBuilder<AppDbContext>();
builder.UseInMemoryDatabase("cleanarchitecture")
.UseInternalServiceProvider(serviceProvider);
return builder.Options;
}
[Fact]
public void AddItemAndSetId()
{
var repository = GetRepository();
var item = new ToDoItemBuilder().Build();
repository.Add(item);
var newItem = repository.List<ToDoItem>().FirstOrDefault();
Assert.Equal(item, newItem);
Assert.True(newItem?.Id > 0);
}
[Fact]
public void UpdateItemAfterAddingIt()
public void UpdatesItemAfterAddingIt()
{
// add an item
var repository = GetRepository();
......@@ -75,31 +38,5 @@ namespace CleanArchitecture.Tests.Integration.Data
Assert.NotEqual(item.Title, updatedItem.Title);
Assert.Equal(newItem.Id, updatedItem.Id);
}
[Fact]
public void DeleteItemAfterAddingIt()
{
// add an item
var repository = GetRepository();
var initialTitle = Guid.NewGuid().ToString();
var item = new ToDoItemBuilder().Title(initialTitle).Build();
repository.Add(item);
// delete the item
repository.Delete(item);
// verify it's no longer there
Assert.DoesNotContain(repository.List<ToDoItem>(),
i => i.Title == initialTitle);
}
private EfRepository GetRepository()
{
var options = CreateNewContextOptions();
var mockDispatcher = new Mock<IDomainEventDispatcher>();
_dbContext = new AppDbContext(options, mockDispatcher.Object);
return new EfRepository(_dbContext);
}
}
}
......@@ -7,9 +7,8 @@ using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
namespace CleanArchitecture.Tests.Integration.Web
namespace CleanArchitecture.Tests.Integration.Web.Api
{
public class ApiToDoItemsControllerList : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
......
using CleanArchitecture.Web;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
namespace CleanArchitecture.Tests.Integration.Web.Api
{
public class MetaControllerInfo : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
public MetaControllerInfo(CustomWebApplicationFactory<Startup> factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task ReturnsVersionAndLastUpdateDate()
{
var response = await _client.GetAsync("/info");
response.EnsureSuccessStatusCode();
var stringResponse = await response.Content.ReadAsStringAsync();
Assert.Contains("Version", stringResponse);
Assert.Contains("Last Updated", stringResponse);
}
}
}
......@@ -5,17 +5,17 @@ using Xunit;
namespace CleanArchitecture.Tests.Integration.Web
{
public class HomeControllerIndexShould : IClassFixture<CustomWebApplicationFactory<Startup>>
public class HomeControllerIndex : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
public HomeControllerIndexShould(CustomWebApplicationFactory<Startup> factory)
public HomeControllerIndex(CustomWebApplicationFactory<Startup> factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task ReturnViewWithCorrectMessage()
public async Task ReturnsViewWithCorrectMessage()
{
HttpResponseMessage response = await _client.GetAsync("/");
response.EnsureSuccessStatusCode();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment