From 4fa5795aea4ab27bf7f34f5a3af2205904e0ebb7 Mon Sep 17 00:00:00 2001 From: Nano Taboada <87288+nanotaboada@users.noreply.github.com> Date: Fri, 19 Apr 2024 01:10:56 -0300 Subject: [PATCH] refactor(services)!: add Player mapping to UpdateAsync, add test cases --- .../PlayerControllerTests.cs | 2 +- .../PlayerServiceTests.cs | 80 +++++++++++++++++++ .../Data/PlayerDataBuilder.cs | 33 ++++---- .../Data/PlayerDataExtensions.cs | 22 +++++ .../Services/PlayerService.cs | 18 +++-- 5 files changed, 132 insertions(+), 23 deletions(-) create mode 100644 Dotnet.Samples.AspNetCore.WebApi/Data/PlayerDataExtensions.cs diff --git a/Dotnet.Samples.AspNetCore.WebApi.Tests/PlayerControllerTests.cs b/Dotnet.Samples.AspNetCore.WebApi.Tests/PlayerControllerTests.cs index 59f865a..c82e5ac 100644 --- a/Dotnet.Samples.AspNetCore.WebApi.Tests/PlayerControllerTests.cs +++ b/Dotnet.Samples.AspNetCore.WebApi.Tests/PlayerControllerTests.cs @@ -63,7 +63,7 @@ public async Task GivenPostAsync_WhenServiceRetrieveByIdAsyncReturnsPlayer_ThenR public async Task GivenPostAsync_WhenServiceRetrieveByIdAsyncReturnsNull_ThenResponseStatusCodeShouldBe201Created() { // Arrange - var player = PlayerDataBuilder.SeedOneById(12); + var player = PlayerDataBuilder.SeedOneNew(); var service = new Mock(); service diff --git a/Dotnet.Samples.AspNetCore.WebApi.Tests/PlayerServiceTests.cs b/Dotnet.Samples.AspNetCore.WebApi.Tests/PlayerServiceTests.cs index 2debd73..e1df041 100644 --- a/Dotnet.Samples.AspNetCore.WebApi.Tests/PlayerServiceTests.cs +++ b/Dotnet.Samples.AspNetCore.WebApi.Tests/PlayerServiceTests.cs @@ -31,6 +31,34 @@ public void Dispose() GC.SuppressFinalize(this); } + /* ------------------------------------------------------------------------- + * Create + * ---------------------------------------------------------------------- */ + + [Fact] + [Trait("Category", "CreateAsync")] + public async Task GivenCreateAsync_WhenInvokedWithPlayer_ThenShouldAddPlayerToContextAndRemovePlayersFromCache() + { + // Arrange + var player = PlayerDataBuilder.SeedOneNew(); + var logger = PlayerMocks.LoggerMock(); + var memoryCache = PlayerMocks.MemoryCacheMock(It.IsAny()); + + var service = new PlayerService(_context, logger.Object, memoryCache.Object); + + // Act + await service.CreateAsync(player); + var result = await _context.Players.FindAsync(player.Id); + + // Assert + result.Should().NotBeNull(); + memoryCache.Verify(cache => cache.Remove(It.IsAny()), Times.Exactly(1)); + } + + /* ------------------------------------------------------------------------- + * Retrieve + * ---------------------------------------------------------------------- */ + [Fact] [Trait("Category", "RetrieveAsync")] public async Task GivenRetrieveAsync_WhenInvoked_ThenShouldReturnAllPlayers() @@ -97,6 +125,58 @@ public async Task GivenRetrieveByIdAsync_WhenInvokedWithPlayerId_ThenShouldRetur result.Should().BeEquivalentTo(player); } + /* ------------------------------------------------------------------------- + * Update + * ---------------------------------------------------------------------- */ + + [Fact] + [Trait("Category", "UpdateAsync")] + public async Task GivenUpdateAsync_WhenInvokedWithPlayer_ThenShouldModifyPlayerInContextAndRemovePlayersFromCache() + { + // Arrange + var player = PlayerDataBuilder.SeedOneById(1); + var logger = PlayerMocks.LoggerMock(); + var memoryCache = PlayerMocks.MemoryCacheMock(It.IsAny()); + + var service = new PlayerService(_context, logger.Object, memoryCache.Object); + + // Act + player.FirstName = "Emiliano"; + player.MiddleName = ""; + await service.UpdateAsync(player); + var result = await _context.Players.FindAsync(player.Id); + + // Assert + result!.FirstName.Should().Be(player.FirstName); + memoryCache.Verify(cache => cache.Remove(It.IsAny()), Times.Exactly(1)); + } + + /* ------------------------------------------------------------------------- + * Delete + * ---------------------------------------------------------------------- */ + + [Fact] + [Trait("Category", "DeleteAsync")] + public async Task GivenDeleteAsync_WhenInvokedWithPlayerId_ThenShouldDeletePlayerInContextAndRemovePlayersFromCache() + { + // Arrange + var player = PlayerDataBuilder.SeedOneNew(); + var logger = PlayerMocks.LoggerMock(); + var memoryCache = PlayerMocks.MemoryCacheMock(It.IsAny()); + await _context.AddAsync(player); + await _context.SaveChangesAsync(); + + var service = new PlayerService(_context, logger.Object, memoryCache.Object); + + // Act + await service.DeleteAsync(player.Id); + var result = await _context.Players.FindAsync(player.Id); + + // Assert + result.Should().BeNull(); + memoryCache.Verify(cache => cache.Remove(It.IsAny()), Times.Exactly(1)); + } + private async Task ExecutionTimeAsync(Func awaitable) { var stopwatch = new Stopwatch(); diff --git a/Dotnet.Samples.AspNetCore.WebApi/Data/PlayerDataBuilder.cs b/Dotnet.Samples.AspNetCore.WebApi/Data/PlayerDataBuilder.cs index 5fa6109..a93d4b3 100644 --- a/Dotnet.Samples.AspNetCore.WebApi/Data/PlayerDataBuilder.cs +++ b/Dotnet.Samples.AspNetCore.WebApi/Data/PlayerDataBuilder.cs @@ -1,5 +1,4 @@ -using System.IO.Compression; -using System.Text.Json; +using System.Text.Json; using Dotnet.Samples.AspNetCore.WebApi.Models; namespace Dotnet.Samples.AspNetCore.WebApi.Data; @@ -11,6 +10,22 @@ public static Player SeedOneById(int id) return SeedWithStarting11().SingleOrDefault(player => player.Id == id) ?? new Player(); } + public static Player SeedOneNew() => + new() + { + Id = 12, + FirstName = "Leandro", + MiddleName = "Daniel", + LastName = "Paredes", + DateOfBirth = new DateTime(1994, 06, 29, 0, 0, 0, DateTimeKind.Utc), + SquadNumber = 5, + Position = "Defensive Midfield", + AbbrPosition = "DM", + Team = "AS Roma", + League = "Serie A", + Starting11 = false + }; + public static List SeedWithStarting11() { var players = new List @@ -165,20 +180,6 @@ public static List SeedWithStarting11() Team = "Manchester City", League = "Premier League", Starting11 = true, - }, - new() - { - Id = 12, - FirstName = "Leandro", - MiddleName = "Daniel", - LastName = "Paredes", - DateOfBirth = new DateTime(1994, 06, 29, 0, 0, 0, DateTimeKind.Utc), - SquadNumber = 5, - Position = "Defensive Midfield", - AbbrPosition = "DM", - Team = "AS Roma", - League = "Serie A", - Starting11 = false } }; diff --git a/Dotnet.Samples.AspNetCore.WebApi/Data/PlayerDataExtensions.cs b/Dotnet.Samples.AspNetCore.WebApi/Data/PlayerDataExtensions.cs new file mode 100644 index 0000000..f6b06b3 --- /dev/null +++ b/Dotnet.Samples.AspNetCore.WebApi/Data/PlayerDataExtensions.cs @@ -0,0 +1,22 @@ +using Dotnet.Samples.AspNetCore.WebApi.Models; + +namespace Dotnet.Samples.AspNetCore.WebApi.Data; + +public static class PlayerDataExtensions +{ + /// + /// Simple extension method to map all properties of a Player + /// + public static void MapFrom(this Player target, Player source) + { + target.FirstName = source.FirstName; + target.MiddleName = source.MiddleName; + target.LastName = source.LastName; + target.DateOfBirth = source.DateOfBirth; + target.SquadNumber = source.SquadNumber; + target.Position = source.Position; + target.Team = source.Team; + target.League = source.League; + target.Starting11 = source.Starting11; + } +} diff --git a/Dotnet.Samples.AspNetCore.WebApi/Services/PlayerService.cs b/Dotnet.Samples.AspNetCore.WebApi/Services/PlayerService.cs index c700518..61cffec 100644 --- a/Dotnet.Samples.AspNetCore.WebApi/Services/PlayerService.cs +++ b/Dotnet.Samples.AspNetCore.WebApi/Services/PlayerService.cs @@ -1,4 +1,5 @@ -using Dotnet.Samples.AspNetCore.WebApi.Models; +using Dotnet.Samples.AspNetCore.WebApi.Data; +using Dotnet.Samples.AspNetCore.WebApi.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; @@ -80,9 +81,14 @@ Use multiple environments in ASP.NET Core public async Task UpdateAsync(Player player) { - if (await _playerContext.Players.FindAsync(player.Id) != null) + var entity = await _playerContext.Players.FindAsync(player.Id); + + if (entity != null) { - _playerContext.Entry(player).State = EntityState.Modified; + // TODO: Add AutoMapper + entity.MapFrom(player); + + _playerContext.Entry(entity).State = EntityState.Modified; await _playerContext.SaveChangesAsync(); _memoryCache.Remove(MemoryCache_Key_RetrieveAsync); } @@ -94,11 +100,11 @@ public async Task UpdateAsync(Player player) public async Task DeleteAsync(long id) { - var player = await _playerContext.Players.FindAsync(id); + var entity = await _playerContext.Players.FindAsync(id); - if (player != null) + if (entity != null) { - _playerContext.Entry(player).State = EntityState.Deleted; + _playerContext.Entry(entity).State = EntityState.Deleted; await _playerContext.SaveChangesAsync(); _memoryCache.Remove(MemoryCache_Key_RetrieveAsync); }