Skip to content

Commit

Permalink
test: update unit test for domain layer (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
phamhongphuc1403 authored Aug 6, 2024
1 parent bd670cb commit e9a7718
Show file tree
Hide file tree
Showing 28 changed files with 1,688 additions and 53 deletions.
9 changes: 9 additions & 0 deletions BuildingBlocks.sln
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{CFE5A713-D
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infrastructure", "Infrastructure", "{9BFF2EA3-E081-4A2B-8297-A028DDD73E4A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{DC3F11D0-5517-44CC-B4D8-D2FB6282B874}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{06A00771-DEB6-4D06-A233-2B10B091295D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -66,6 +70,10 @@ Global
{C9891FE2-E5C6-4773-BCA0-919FDCC132D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9891FE2-E5C6-4773-BCA0-919FDCC132D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9891FE2-E5C6-4773-BCA0-919FDCC132D2}.Release|Any CPU.Build.0 = Release|Any CPU
{DC3F11D0-5517-44CC-B4D8-D2FB6282B874}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DC3F11D0-5517-44CC-B4D8-D2FB6282B874}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC3F11D0-5517-44CC-B4D8-D2FB6282B874}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC3F11D0-5517-44CC-B4D8-D2FB6282B874}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{0C2BE5AF-98DD-4E1D-A385-98E6B2D8A3A2} = {37ABC0F6-39B9-4057-955A-BA2CFDB64811}
Expand All @@ -77,5 +85,6 @@ Global
{4E1A0628-01F4-4876-B943-AC9FAFB19238} = {9BFF2EA3-E081-4A2B-8297-A028DDD73E4A}
{2667E7B6-8451-4842-9CC5-0663C8CAE5F4} = {9BFF2EA3-E081-4A2B-8297-A028DDD73E4A}
{C9891FE2-E5C6-4773-BCA0-919FDCC132D2} = {9BFF2EA3-E081-4A2B-8297-A028DDD73E4A}
{DC3F11D0-5517-44CC-B4D8-D2FB6282B874} = {06A00771-DEB6-4D06-A233-2B10B091295D}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace BuildingBlock.Core.Application.EventBus.Implementations;

public class EventBusSubscriptionsManager : IEventBusSubscriptionsManager
{
private readonly List<Type> _eventTypes = new();
private readonly List<Type> _eventTypes = [];
private readonly Dictionary<string, List<Type>> _handlers = new();

public bool IsEmpty => _handlers is { Count: 0 };
Expand Down Expand Up @@ -63,7 +63,7 @@ public string GetEventKey<T>()

private void DoAddSubscription(Type handlerType, string eventName)
{
if (!HasSubscriptionsForEvent(eventName)) _handlers.Add(eventName, new List<Type>());
if (!HasSubscriptionsForEvent(eventName)) _handlers.Add(eventName, []);

if (_handlers[eventName].Any(type => type == handlerType))
throw new ArgumentException(
Expand Down
5 changes: 3 additions & 2 deletions Core/BuildingBlock.Core.Domain/BaseEntity.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
Expand Down Expand Up @@ -91,7 +92,7 @@ private void DeleteProperty(PropertyInfo propertyInfo, DateTime? deletedAt, stri

foreach (var value in propertyValues)
{
object?[] parameters = { deletedAt, deletedBy };
object?[] parameters = [deletedAt, deletedBy];
deleteMethod.Invoke(value, parameters);
}
}
Expand All @@ -118,7 +119,7 @@ private static bool IsAGenericList(Type type)

public abstract class AggregateRoot : Entity, IAggregateRoot
{
public List<IDomainEvent> DomainEvents { get; } = new();
public List<IDomainEvent> DomainEvents { get; } = [];

public void AddDomainEvent(IDomainEvent domainEvent)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protected EntityNotFoundException(string entity, string id) : base(
{
}

public EntityNotFoundException(string message) : base(message)
protected EntityNotFoundException(string message) : base(message)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ namespace BuildingBlock.Core.Domain.Rules.Abstractions;

public interface IBusinessRule
{
string Message { get; }
string? Message { get; }
bool IsBroken();
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public EmailAddressMustFollowPattern(string email)
_email = email;
}

public string? Message { get; private set; }

public bool IsBroken()
{
try
Expand All @@ -21,9 +23,13 @@ public bool IsBroken()
}
catch (FormatException)
{
Message = $"Email address: '{_email}' is not valid.";
return true;
}
catch (ArgumentException)
{
Message = $"Email address: '{_email}' can not be empty.";
return true;
}
}

public string Message => $"Email address: '{_email}' is not valid.";
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@ public class StringCanNotBeEmptyOrWhiteSpaceRule : IBusinessRule
private readonly string _name;
private readonly string? _value;

protected StringCanNotBeEmptyOrWhiteSpaceRule(string? value, string name)

public StringCanNotBeEmptyOrWhiteSpaceRule(string? value, string name)
{
_value = value;
_name = name;
}

public string? Message { get; private set; }

public bool IsBroken()
{
return string.IsNullOrEmpty(_value) || string.IsNullOrWhiteSpace(_value);
}
if (!string.IsNullOrEmpty(_value) && !string.IsNullOrWhiteSpace(_value)) return false;

public string Message => $"{_name} can not be empty or contain only white spaces.";
Message = $"{_name} with value: {_value} can not be empty or contain only white spaces.";
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,6 @@ public static T BindAndGetConfig<T>(this IConfiguration configuration, string se

if (config == null) throw new Exception($"{sectionName} configuration is not provided.");

CheckForNullProperties(config, sectionName);

return config;
}

private static void CheckForNullProperties(object obj, string sectionName)
{
var properties = obj.GetType().GetProperties();

foreach (var property in properties)
{
var value = property.GetValue(obj, null);

if (value == null)
throw new Exception($"Property '{property.Name}' in section '{sectionName}' is missing.");
}
}
}
22 changes: 9 additions & 13 deletions Core/BuildingBlock.Core.Domain/Shared/Utils/Optional.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,26 @@ public Optional<T> ThrowIfNotExist(Exception exception)

public T Get()
{
CheckIfInstanceIsNotNull();
CheckIfExist();
return _instance!;
}

public Optional<T> ThrowIfNotEqual(T value, Exception exception)
public Optional<T> ThrowIfNotEqual(T? value, Exception exception)
{
CheckIfInstanceIsNotNull();
if (Equals(_instance, value)) return this;

if (!_instance!.Equals(value)) throw exception;

return this;
throw exception;
}

public Optional<T> ThrowIfEqual(T value, Exception exception)
public Optional<T> ThrowIfEqual(T? value, Exception exception)
{
CheckIfInstanceIsNotNull();
if (!Equals(_instance, value)) return this;

if (_instance!.Equals(value)) throw exception;

return this;
throw exception;
}

private void CheckIfInstanceIsNotNull()
private void CheckIfExist()
{
if (_instance == null) throw new InvalidOperationException("No value present");
ThrowIfNotExist(new InvalidOperationException("Instance is null."));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,13 @@ public bool Equals(ValueObject? other)

protected abstract IEnumerable<object?> GetValues();

public override bool Equals(object? obj)
{
return obj is ValueObject other && EqualsTo(other);
}

public override int GetHashCode()
{
return GetValues().Aggregate(default(int), HashCode.Combine);
}

private bool EqualsTo(ValueObject other)
{
return GetValues().SequenceEqual(other.GetValues());
}

public static void CheckRule(IBusinessRule rule)
{
if (rule.IsBroken()) throw new ValidationException(rule.Message);
if (rule.IsBroken()) throw new ValidationException(rule.Message!);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static class FilterExtension
public static void SetSoftDeleteFilter(this ModelBuilder modelBuilder, Type entityType)
{
SetSoftDeleteFilterMethod.MakeGenericMethod(entityType)
.Invoke(null, new object[] { modelBuilder });
.Invoke(null, [modelBuilder]);
}

public static void SetSoftDeleteFilter<TEntity>(this ModelBuilder modelBuilder)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ private async Task ProcessEvent(string eventName, string message)

await Task.Yield();

await (Task)concreteType.GetMethod("HandleAsync")?.Invoke(handler, new[] { integrationEvent })!;
await (Task)concreteType.GetMethod("HandleAsync")?.Invoke(handler, [integrationEvent])!;
}
}
else
Expand Down
130 changes: 130 additions & 0 deletions Tests/Core/Domain/BaseEntityTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using BuildingBlock.Core.Domain;
using FluentAssertions;

namespace Tests.Core.Domain;

public class EntityTest
{
public class TestEntity : Entity
{
public TestEntity()
{
UpdatedAt = DateTime.Now;
UpdatedBy = "Test";
}

public List<TestEntity> TestEntities { get; set; } = [];
public List<int> TestInts { get; set; } = [1, 2, 3];
public List<string> TestStrings { get; set; } = [];
public TestEntity ChildEntity { get; set; } = null!;
}

public class ResetUpdatedTimeStamp
{
public class ShouldResetUpdatedTimeStamp
{
[Fact]
public void WhenInvoke()
{
// Arrange
var entity = new TestEntity();

// Act
entity.ResetUpdatedTimeStamp();

// Assert
entity.UpdatedAt.Should().BeNull();
entity.UpdatedBy.Should().BeNull();
}
}
}
}

public class AggregateRootTest
{
public class AddDomainEvent
{
public class ShouldAddDomainEvent
{
[Fact]
public void WhenInvoke()
{
// Arrange
var aggregateRoot = new TestAggregateRoot();
var domainEvent = new TestDomainEvent();

// Act
aggregateRoot.AddDomainEvent(domainEvent);

// Assert
aggregateRoot.DomainEvents.Should().Contain(domainEvent);
aggregateRoot.DomainEvents.Should().HaveCount(1);
}
}
}

public class RemoveDomainEvent
{
public class ShouldRemoveDomainEvent
{
[Fact]
public void WhenThatDomainEventIsExist()
{
// Arrange
var aggregateRoot = new TestAggregateRoot();
var domainEvent = new TestDomainEvent();
aggregateRoot.AddDomainEvent(domainEvent);

// Act
aggregateRoot.RemoveDomainEvent(domainEvent);

// Assert
aggregateRoot.DomainEvents.Should().NotContain(domainEvent);
aggregateRoot.DomainEvents.Should().HaveCount(0);
}
}

public class ShouldDoNothing
{
[Fact]
public void WhenThatDomainEventIsNotExist()
{
// Arrange
var aggregateRoot = new TestAggregateRoot();
var domainEvent = new TestDomainEvent();

// Act
aggregateRoot.RemoveDomainEvent(domainEvent);

// Assert
aggregateRoot.DomainEvents.Should().NotContain(domainEvent);
aggregateRoot.DomainEvents.Should().HaveCount(0);
}
}
}

public class ClearDomainEvent
{
public class ShouldRemoveAllDomainEvents
{
[Fact]
public void WhenInvoke()
{
// Arrange
var aggregateRoot = new TestAggregateRoot();
var domainEvent = new TestDomainEvent();
aggregateRoot.AddDomainEvent(domainEvent);
aggregateRoot.AddDomainEvent(domainEvent);
aggregateRoot.AddDomainEvent(domainEvent);
aggregateRoot.AddDomainEvent(domainEvent);

// Act
aggregateRoot.ClearDomainEvents();

// Assert
aggregateRoot.DomainEvents.Should().NotContain(domainEvent);
aggregateRoot.DomainEvents.Should().HaveCount(0);
}
}
}
}
Loading

0 comments on commit e9a7718

Please sign in to comment.