Skip to content

Commit

Permalink
Add Architecture Tests for Application layer and Domain layer for Mee…
Browse files Browse the repository at this point in the history
…tings module
  • Loading branch information
kgrzybek committed Nov 18, 2019
1 parent 385184a commit eba6302
Show file tree
Hide file tree
Showing 41 changed files with 572 additions and 99 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using CompanyName.MyMeetings.Modules.Meetings.Application.Configuration.Processing;
using CompanyName.MyMeetings.Modules.Meetings.Application.Configuration.Processing.InternalCommands;
using CompanyName.MyMeetings.Modules.Meetings.Application.Contracts;
using CompanyName.MyMeetings.Modules.Meetings.ArchitectureTests.SeedWork;
using NetArchTest.Rules;
using Newtonsoft.Json;
using NUnit.Framework;

namespace CompanyName.MyMeetings.Modules.Meetings.ArchitectureTests.Application
{
[TestFixture]
public class ApplicationTests : TestBase
{
[Test]
public void Command_Should_Be_Immutable()
{
var types = Types.InAssembly(ApplicationAssembly)
.That().Inherit(typeof(CommandBase))
.Or().Inherit(typeof(InternalCommandBase))
.Or().ImplementInterface(typeof(ICommand))
.Or().ImplementInterface(typeof(ICommand<>))
.GetTypes();

AssertAreImmutable(types);
}

[Test]
public void Query_Should_Be_Immutable()
{
var types = Types.InAssembly(ApplicationAssembly)
.That().ImplementInterface(typeof(IQuery<>)).GetTypes();

AssertAreImmutable(types);
}

[Test]
public void CommandHandler_Should_Have_Name_EndingWith_CommandHandler()
{
var result = Types.InAssembly(ApplicationAssembly)
.That()
.ImplementInterface(typeof(ICommandHandler<>))
.And()
.DoNotHaveNameMatching(".*Decorator.*").Should()
.HaveNameEndingWith("CommandHandler")
.GetResult();

AssertArchTestResult(result);
}

[Test]
public void QueryHandler_Should_Have_Name_EndingWith_QueryHandler()
{
var result = Types.InAssembly(ApplicationAssembly)
.That()
.ImplementInterface(typeof(IQueryHandler<,>))
.Should()
.HaveNameEndingWith("QueryHandler")
.GetResult();

AssertArchTestResult(result);
}

[Test]
public void InternalCommands_Should_Not_Be_Public()
{
var result = Types.InAssembly(ApplicationAssembly)
.That()
.Inherit(typeof(InternalCommandBase))
.Should()
.NotBePublic()
.GetResult();

AssertArchTestResult(result);
}

[Test]
public void Command_And_Query_Handlers_Should_Not_Be_Public()
{
var types = Types.InAssembly(ApplicationAssembly)
.That()
.ImplementInterface(typeof(IQueryHandler<,>))
.Or()
.ImplementInterface(typeof(ICommandHandler<>))
.Should().NotBePublic().GetResult().FailingTypes;

AssertFailingTypes(types);
}

[Test]
public void InternalCommand_Should_Have_Internal_Constructor_With_JsonConstructorAttribute()
{
var types = Types.InAssembly(ApplicationAssembly)
.That().Inherit(typeof(InternalCommandBase)).GetTypes();

var failingTypes = new List<Type>();

foreach (var type in types)
{
bool hasJsonConstructorDefined = false;
var constructors = type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var constructorInfo in constructors)
{
var jsonConstructorAttribute = constructorInfo.GetCustomAttributes(typeof(JsonConstructorAttribute), false);
if (jsonConstructorAttribute.Length > 0)
{
hasJsonConstructorDefined = true;
break;
}
}

if (!hasJsonConstructorDefined)
{
failingTypes.Add(type);
}
}

AssertFailingTypes(failingTypes);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>

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

<ItemGroup>
<PackageReference Include="Autofac" Version="4.9.4" />
<PackageReference Include="NetArchTest.Rules" Version="1.2.3" />
<PackageReference Include="nunit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\BuildingBlocks\Domain\CompanyName.MyMeetings.BuildingBlocks.Domain.csproj" />
<ProjectReference Include="..\Modules\Meetings\Application\CompanyName.MyMeetings.Modules.Meetings.Application.csproj" />
<ProjectReference Include="..\Modules\Meetings\Domain\CompanyName.MyMeetings.Modules.Meetings.Domain.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using CompanyName.MyMeetings.BuildingBlocks.Domain;
using CompanyName.MyMeetings.Modules.Meetings.ArchitectureTests.SeedWork;
using NetArchTest.Rules;
using NUnit.Framework;

namespace CompanyName.MyMeetings.Modules.Meetings.ArchitectureTests.Domain
{
public class DomainTests : TestBase
{
[Test]
public void DomainEvent_Should_Be_Immutable()
{
var types = Types.InAssembly(DomainAssembly)
.That()
.Inherit(typeof(DomainEventBase))
.Or()
.Inherit(typeof(IDomainEvent))
.GetTypes();

AssertAreImmutable(types);
}

[Test]
public void ValueObject_Should_Be_Immutable()
{
var types = Types.InAssembly(DomainAssembly)
.That()
.Inherit(typeof(ValueObject))
.GetTypes();

AssertAreImmutable(types);
}

[Test]
public void Entity_Which_Is_Not_Aggregate_Root_Cannot_Have_Public_Members()
{
var types = Types.InAssembly(DomainAssembly)
.That()
.Inherit(typeof(Entity))
.And().DoNotImplementInterface(typeof(IAggregateRoot)).GetTypes();

const BindingFlags bindingFlags = BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.Static;

var failingTypes = new List<Type>();
foreach (var type in types)
{
var publicFields = type.GetFields(bindingFlags);
var publicProperties = type.GetProperties(bindingFlags);
var publicMethods = type.GetMethods(bindingFlags);

if (publicFields.Any() || publicProperties.Any() || publicMethods.Any())
{
failingTypes.Add(type);
}
}

AssertFailingTypes(failingTypes);
}

[Test]
public void Entity_Cannot_Have_Reference_To_Other_AggregateRoot()
{
var entityTypes = Types.InAssembly(DomainAssembly)
.That()
.Inherit(typeof(Entity)).GetTypes();

var aggregateRoots = Types.InAssembly(DomainAssembly)
.That().ImplementInterface(typeof(IAggregateRoot)).GetTypes().ToList();

const BindingFlags bindingFlags = BindingFlags.DeclaredOnly |
BindingFlags.NonPublic |
BindingFlags.Instance;

var failingTypes = new List<Type>();
foreach (var type in entityTypes)
{
var fields = type.GetFields(bindingFlags);

foreach (var field in fields)
{
if (aggregateRoots.Contains(field.FieldType) ||
field.FieldType.GenericTypeArguments.Any(x => aggregateRoots.Contains(x)))
{
failingTypes.Add(type);
break;
}
}

var properties = type.GetProperties(bindingFlags);
foreach (var property in properties)
{
if (aggregateRoots.Contains(property.PropertyType) ||
property.PropertyType.GenericTypeArguments.Any(x => aggregateRoots.Contains(x)))
{
failingTypes.Add(type);
break;
}
}
}

AssertFailingTypes(failingTypes);
}

[Test]
public void Entity_Should_Have_Parameterless_Private_Constructor()
{
var entityTypes = Types.InAssembly(DomainAssembly)
.That()
.Inherit(typeof(Entity)).GetTypes();

var failingTypes = new List<Type>();
foreach (var entityType in entityTypes)
{
bool hasPrivateParameterlessConstructor = false;
var constructors = entityType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var constructorInfo in constructors)
{
if (constructorInfo.IsPrivate && constructorInfo.GetParameters().Length == 0)
{
hasPrivateParameterlessConstructor = true;
}
}

if (!hasPrivateParameterlessConstructor)
{
failingTypes.Add(entityType);
}
}

AssertFailingTypes(failingTypes);
}

[Test]
public void Domain_Object_Should_Have_Only_Private_Constructors()
{
var domainObjectTypes = Types.InAssembly(DomainAssembly)
.That()
.Inherit(typeof(Entity))
.Or()
.Inherit(typeof(ValueObject))
.GetTypes();

var failingTypes = new List<Type>();
foreach (var domainObjectType in domainObjectTypes)
{
var constructors = domainObjectType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
foreach (var constructorInfo in constructors)
{

if (!constructorInfo.IsPrivate)
{
failingTypes.Add(domainObjectType);
}
}
}

AssertFailingTypes(failingTypes);
}

[Test]
public void ValueObject_Should_Have_Private_Constructor_With_Parameters_For_His_State()
{
var valueObjects = Types.InAssembly(DomainAssembly)
.That()
.Inherit(typeof(ValueObject)).GetTypes();

var failingTypes = new List<Type>();
foreach (var entityType in valueObjects)
{
bool hasExpectedConstructor = false;

const BindingFlags bindingFlags = BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance;
var names = entityType.GetFields(bindingFlags).Select(x => x.Name.ToLower()).ToList();
var propertyNames = entityType.GetProperties(bindingFlags).Select(x => x.Name.ToLower()).ToList();
names.AddRange(propertyNames);
var constructors = entityType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var constructorInfo in constructors)
{
var parameters = constructorInfo.GetParameters().Select( x => x.Name.ToLower()).ToList();

if (names.Intersect(parameters).Count() == names.Count)
{
hasExpectedConstructor = true;
break;
}
}

if (!hasExpectedConstructor)
{
failingTypes.Add(entityType);
}
}

AssertFailingTypes(failingTypes);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using CompanyName.MyMeetings.Modules.Meetings.Application.Contracts;
using CompanyName.MyMeetings.Modules.Meetings.Domain.MeetingGroupProposals;
using NetArchTest.Rules;
using NUnit.Framework;

namespace CompanyName.MyMeetings.Modules.Meetings.ArchitectureTests.SeedWork
{
public abstract class TestBase
{
protected static Assembly ApplicationAssembly = typeof(CommandBase).Assembly;
protected static Assembly DomainAssembly = typeof(MeetingGroupProposal).Assembly;

protected static void AssertAreImmutable(IEnumerable<Type> types)
{
IList<Type> failingTypes = new List<Type>();
foreach (var type in types)
{
if (type.GetFields().Any(x => !x.IsInitOnly) || type.GetProperties().Any(x => x.CanWrite))
{
failingTypes.Add(type);
break;
}
}

AssertFailingTypes(failingTypes);
}
protected static void AssertFailingTypes(IEnumerable<Type> types)
{
Assert.That(types, Is.Null.Or.Empty);
}

protected static void AssertArchTestResult(TestResult result)
{
AssertFailingTypes(result.FailingTypes);
}
}
}
Loading

0 comments on commit eba6302

Please sign in to comment.