test domain events (#2)

* test domain events

* cleanup
This commit is contained in:
2022-10-10 12:06:08 +03:00
committed by GitHub
parent 9076dcc542
commit 8d68f1b7a1
13 changed files with 140 additions and 20 deletions
Binary file not shown.
@@ -1,6 +1,9 @@
using MapsterMapper; using Mapster;
using MapsterMapper;
using MediatR; using MediatR;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using PracticeCalendar.Application.PracticeEvents.Queries;
using PracticeCalendar.Domain.Entities;
using System.Reflection; using System.Reflection;
namespace PracticeCalendar.Application namespace PracticeCalendar.Application
@@ -9,10 +12,25 @@ namespace PracticeCalendar.Application
{ {
public static IServiceCollection AddApplicationServices(this IServiceCollection services) public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{ {
services.AddSingleton<IMapper>(new Mapper(new Mapster.TypeAdapterConfig())); services.AddMapsterMappings();
services.AddMediatR(Assembly.GetExecutingAssembly()); services.AddMediatR(Assembly.GetExecutingAssembly());
return services; return services;
} }
private static IServiceCollection AddMapsterMappings(this IServiceCollection services)
{
TypeAdapterConfig.GlobalSettings.Default.MapToConstructor(true);
TypeAdapterConfig.GlobalSettings.NewConfig<PracticeEventDto, PracticeEvent>()
.ConstructUsing(src => new PracticeEvent(src.Title, src.Description, src.StartTime, src.EndTime));
var mapsterConfig = new TypeAdapterConfig();
mapsterConfig.NewConfig<PracticeEventDto, PracticeEvent>()
.MapToConstructor(true)
.ConstructUsing(src => new PracticeEvent(src.Title, src.Description, src.StartTime, src.EndTime));
services.AddSingleton<IMapper>(new Mapper(mapsterConfig));
return services;
}
} }
} }
@@ -28,7 +28,7 @@ namespace PracticeCalendar.Application.PracticeEvents.Commands
public async Task<PracticeEventDto> Handle(CreatePracticeEventCommand request, CancellationToken cancellationToken) public async Task<PracticeEventDto> Handle(CreatePracticeEventCommand request, CancellationToken cancellationToken)
{ {
var input = request.Event; var input = request.Event;
var practiceEvent = new PracticeEvent(input.Title, input.Description); var practiceEvent = new PracticeEvent(input.Title, input.Description, input.StartTime, input.EndTime);
foreach (var att in input.Attendees) foreach (var att in input.Attendees)
{ {
practiceEvent.AddAttendee(new Attendee(att.Name, att.EmailAddress)); practiceEvent.AddAttendee(new Attendee(att.Name, att.EmailAddress));
@@ -11,12 +11,14 @@ namespace PracticeCalendar.Domain.Entities
/// </summary> /// </summary>
public class PracticeEvent : EntityBase, IAggregateRoot public class PracticeEvent : EntityBase, IAggregateRoot
{ {
public PracticeEvent(string title, string description) public PracticeEvent(string title, string description, DateTime startTime, DateTime endTime)
{ {
Guard.Against.NullOrEmpty(title, nameof(title)); Guard.Against.NullOrEmpty(title, nameof(title));
Guard.Against.NullOrEmpty(description, nameof(description)); Guard.Against.NullOrEmpty(description, nameof(description));
this.Title = title; this.Title = title;
this.Description = description; this.Description = description;
this.StartTime = startTime;
this.EndTime = endTime;
} }
public string Title { get; private set; } = string.Empty; public string Title { get; private set; } = string.Empty;
@@ -18,7 +18,7 @@ namespace PracticeCalendar.Infrastructure
services.AddDbContext(connectionString); services.AddDbContext(connectionString);
services.AddTransient<IEmailSender, FileEmailSender>(); services.AddTransient<IEmailSender, FileEmailSender>();
services.AddTransient<IDomainEventService, DomainEventService>(); services.AddSingleton<IDomainEventService, DomainEventService>();
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>)); services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
@@ -56,7 +56,9 @@ namespace PracticeCalendar.Infrastructure.Persistence
// Seed, if necessary // Seed, if necessary
if (!context.PracticeEvents.Any()) if (!context.PracticeEvents.Any())
{ {
context.PracticeEvents.Add(new PracticeEvent("Event 1", "Event 1 desc")); context.PracticeEvents.Add(new PracticeEvent("Event 1", "Event 1 desc",
DateTime.Now.AddHours(-1),
DateTime.Now.AddHours(1)));
await context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
@@ -0,0 +1,74 @@
using FluentAssertions;
using Mapster;
using PracticeCalendar.Application.PracticeEvents.Queries;
using PracticeCalendar.Domain.Entities;
namespace PracticeCalendar.UnitTests.Application.Mapping
{
public class PracticeEventMappingTests
{
readonly string _eventTitle = "Event1";
readonly string _eventDescription = "Description";
readonly string _attendeeName = "Claudiu Farcas";
readonly string _attendeeEmail = "claudiu.farcas@testingbee.com";
readonly DateTime _eventStartTime = new DateTime(2022, 10, 10, 10, 25, 35);
readonly DateTime _eventEndTime = new DateTime(2022, 10, 12, 9, 15, 00);
public PracticeEventMappingTests()
{
TypeAdapterConfig.GlobalSettings.Default.MapToConstructor(true);
TypeAdapterConfig.GlobalSettings.NewConfig<PracticeEventDto, PracticeEvent>()
.ConstructUsing(src => new PracticeEvent(src.Title, src.Description, src.StartTime, src.EndTime));
}
[Fact]
public void PracticeEvent_to_PracticeEventDto()
{
var practiceEvent = new PracticeEvent(_eventTitle, _eventDescription, _eventStartTime, _eventEndTime);
var testAttendee = new Attendee(_attendeeName, _attendeeEmail);
practiceEvent.AddAttendee(testAttendee);
var dto = practiceEvent.Adapt<PracticeEventDto>();
dto.Should().NotBeNull();
dto.Title.Should().Be(_eventTitle);
dto.Description.Should().Be(_eventDescription);
dto.StartTime.Should().Be(_eventStartTime);
dto.EndTime.Should().Be(_eventEndTime);
dto.Attendees.Should().HaveCount(1);
dto.Attendees[0].Name.Should().Be(_attendeeName);
dto.Attendees[0].EmailAddress.Should().Be(_attendeeEmail);
dto.Attendees[0].IsAttending.Should().BeFalse();
}
[Fact]
public void PracticeEventDto_to_PracticeEvent()
{
var practiceEventDto = new PracticeEventDto
{
Title = _eventTitle,
Description = _eventDescription,
StartTime = _eventStartTime,
EndTime = _eventEndTime
};
var attendeeDto = new AttendeeDto
{
Name = _attendeeName,
EmailAddress = _attendeeEmail
};
practiceEventDto.Attendees.Add(attendeeDto);
var entity = practiceEventDto.Adapt<PracticeEvent>();
entity.Should().NotBeNull();
entity.Title.Should().Be(_eventTitle);
entity.Description.Should().Be(_eventDescription);
entity.StartTime.Should().Be(_eventStartTime);
entity.EndTime.Should().Be(_eventEndTime);
entity.Attendees.Should().HaveCount(1);
entity.Attendees[0].Name.Should().Be(_attendeeName);
entity.Attendees[0].EmailAddress.Should().Be(_attendeeEmail);
entity.Attendees[0].IsAttending.Should().BeFalse();
}
}
}
@@ -1,4 +1,5 @@
using FluentAssertions; using FluentAssertions;
using FluentAssertions.Common;
using PracticeCalendar.Domain.Entities; using PracticeCalendar.Domain.Entities;
namespace PracticeCalendar.UnitTests.Domain namespace PracticeCalendar.UnitTests.Domain
@@ -13,7 +14,7 @@ namespace PracticeCalendar.UnitTests.Domain
[Fact] [Fact]
public void InitializeProperties() public void InitializeProperties()
{ {
var practiceEvent = new PracticeEvent(_eventTitle, _eventDescription); var practiceEvent = new PracticeEvent(_eventTitle, _eventDescription, DateTime.Now, DateTime.Now);
practiceEvent.Title.Should().Be(_eventTitle); practiceEvent.Title.Should().Be(_eventTitle);
practiceEvent.Description.Should().Be(_eventDescription); practiceEvent.Description.Should().Be(_eventDescription);
practiceEvent.Attendees.Should().HaveCount(0); practiceEvent.Attendees.Should().HaveCount(0);
@@ -24,25 +25,25 @@ namespace PracticeCalendar.UnitTests.Domain
{ {
Action act = () => Action act = () =>
{ {
var practiceEvent = new PracticeEvent(null!, _eventDescription); var practiceEvent = new PracticeEvent(null!, _eventDescription, DateTime.Now, DateTime.Now);
}; };
act.Should().Throw<ArgumentNullException>(); act.Should().Throw<ArgumentNullException>();
act = () => act = () =>
{ {
var practiceEvent = new PracticeEvent(_eventTitle, null!); var practiceEvent = new PracticeEvent(_eventTitle, null!, DateTime.Now, DateTime.Now);
}; };
act.Should().Throw<ArgumentNullException>(); act.Should().Throw<ArgumentNullException>();
act = () => act = () =>
{ {
var practiceEvent = new PracticeEvent(string.Empty, _eventDescription); var practiceEvent = new PracticeEvent(string.Empty, _eventDescription, DateTime.Now, DateTime.Now);
}; };
act.Should().Throw<ArgumentException>(); act.Should().Throw<ArgumentException>();
act = () => act = () =>
{ {
var practiceEvent = new PracticeEvent(_eventTitle, string.Empty); var practiceEvent = new PracticeEvent(_eventTitle, string.Empty, DateTime.Now, DateTime.Now);
}; };
act.Should().Throw<ArgumentException>(); act.Should().Throw<ArgumentException>();
} }
@@ -50,7 +51,7 @@ namespace PracticeCalendar.UnitTests.Domain
[Fact] [Fact]
public void AddAttendeeToEvent() public void AddAttendeeToEvent()
{ {
var practiceEvent = new PracticeEvent(_eventTitle, _eventDescription); var practiceEvent = new PracticeEvent(_eventTitle, _eventDescription, DateTime.Now, DateTime.Now);
var testAttendee = new Attendee(_attendeeName, _atendeeEmail); var testAttendee = new Attendee(_attendeeName, _atendeeEmail);
practiceEvent.AddAttendee(testAttendee); practiceEvent.AddAttendee(testAttendee);
practiceEvent.Attendees.Should().HaveCount(1); practiceEvent.Attendees.Should().HaveCount(1);
@@ -3,12 +3,21 @@ using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq;
using PracticeCalendar.Domain.Common.Interfaces;
using PracticeCalendar.Infrastructure.Persistence; using PracticeCalendar.Infrastructure.Persistence;
using PracticeCalendar.Infrastructure.Services;
namespace PracticeCalendar.UnitTests.Integration namespace PracticeCalendar.UnitTests.Integration
{ {
public class CustomWebApplicationFactory : WebApplicationFactory<Program> public class CustomWebApplicationFactory : WebApplicationFactory<Program>
{ {
private readonly Action<IServiceCollection> configServices;
public CustomWebApplicationFactory(Action<IServiceCollection> configServices)
{
this.configServices = configServices;
}
protected override void ConfigureWebHost(IWebHostBuilder builder) protected override void ConfigureWebHost(IWebHostBuilder builder)
{ {
builder.ConfigureAppConfiguration(configurationBuilder => builder.ConfigureAppConfiguration(configurationBuilder =>
@@ -27,7 +36,10 @@ namespace PracticeCalendar.UnitTests.Integration
services.AddDbContext<ApplicationDbContext>(options => services.AddDbContext<ApplicationDbContext>(options =>
options.UseInMemoryDatabase("InMemoryDbForTesting") options.UseInMemoryDatabase("InMemoryDbForTesting")
); );
configServices(services);
}); });
} }
} }
} }
@@ -1,7 +1,8 @@
using FluentAssertions; using FluentAssertions;
using Moq;
using PracticeCalendar.Application.PracticeEvents.Commands; using PracticeCalendar.Application.PracticeEvents.Commands;
using PracticeCalendar.Application.PracticeEvents.Queries; using PracticeCalendar.Application.PracticeEvents.Queries;
using PracticeCalendar.Domain.Common;
using static PracticeCalendar.UnitTests.Integration.Testing; using static PracticeCalendar.UnitTests.Integration.Testing;
namespace PracticeCalendar.UnitTests.Integration.PracticeEvents namespace PracticeCalendar.UnitTests.Integration.PracticeEvents
@@ -38,6 +39,9 @@ namespace PracticeCalendar.UnitTests.Integration.PracticeEvents
result.Should().NotBeNull(); result.Should().NotBeNull();
result.Id.Should().NotBe(0); result.Id.Should().NotBe(0);
result.Attendees.Count.Should().Be(2); result.Attendees.Count.Should().Be(2);
//check domain events count
domainEventServiceMock.Verify(x=>x.Publish(It.IsAny<DomainEventBase>()), Times.Exactly(2));
} }
} }
} }
@@ -26,7 +26,8 @@ namespace PracticeCalendar.UnitTests.Integration.PracticeEvents
{ {
await RunBeforeAnyTests(); await RunBeforeAnyTests();
await AddAsync(new PracticeEvent("Test Event", "Event description") await AddAsync(new PracticeEvent("Test Event", "Event description",
DateTime.Now.AddHours(-1), DateTime.Now.AddHours(1))
{ {
Id = 1, Id = 1,
Attendees = { Attendees = {
@@ -5,6 +5,8 @@ using Microsoft.Extensions.DependencyInjection;
using PracticeCalendar.Infrastructure.Persistence; using PracticeCalendar.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using PracticeCalendar.Domain.Entities; using PracticeCalendar.Domain.Entities;
using PracticeCalendar.Domain.Common.Interfaces;
using Moq;
namespace PracticeCalendar.UnitTests.Integration namespace PracticeCalendar.UnitTests.Integration
{ {
@@ -14,9 +16,17 @@ namespace PracticeCalendar.UnitTests.Integration
private static IConfiguration _configuration = null!; private static IConfiguration _configuration = null!;
private static IServiceScopeFactory _scopeFactory = null!; private static IServiceScopeFactory _scopeFactory = null!;
public static Mock<IDomainEventService> domainEventServiceMock = null!;
public static async Task RunBeforeAnyTests() public static async Task RunBeforeAnyTests()
{ {
_factory = new CustomWebApplicationFactory(); domainEventServiceMock = new Mock<IDomainEventService>();
_factory = new CustomWebApplicationFactory(cfg =>
{
cfg.AddSingleton(svc => domainEventServiceMock.Object);
});
_scopeFactory = _factory.Services.GetRequiredService<IServiceScopeFactory>(); _scopeFactory = _factory.Services.GetRequiredService<IServiceScopeFactory>();
_configuration = _factory.Services.GetRequiredService<IConfiguration>(); _configuration = _factory.Services.GetRequiredService<IConfiguration>();
} }
@@ -41,10 +41,6 @@
<ProjectReference Include="..\PracticeCalendar.Api\PracticeCalendar.API.csproj" /> <ProjectReference Include="..\PracticeCalendar.Api\PracticeCalendar.API.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Application\" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="xunit.runner.json"> <None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>