Skip to content

Commit

Permalink
refactor(api)!: adjust environment-specific settings and test mocks
Browse files Browse the repository at this point in the history
  • Loading branch information
nanotaboada committed Apr 19, 2024
1 parent ae9e11c commit 471dc0e
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 73 deletions.
25 changes: 11 additions & 14 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,31 @@
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md.
"name": ".NET Core Launch (web)",
"name": "Debug: ASP.NET Core (Swagger)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/net7.0/Dotnet.Samples.AspNetCore.WebApi.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"program": "${workspaceFolder}/Dotnet.Samples.AspNetCore.WebApi/bin/Debug/net8.0/Dotnet.Samples.AspNetCore.WebApi.dll",
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
"pattern": "\\bNow listening on:\\s+https://localhost:([0-9]+)",
"uriFormat": "https://localhost:%s/swagger"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "https://localhost:9000",
"ASPNETCORE_DETAILEDERRORS": "1",
"ASPNETCORE_SHUTDOWNTIMEOUTSECONDS": "3"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"name": "Attach: ASP.NET Core",
"type": "coreclr",
"request": "attach"
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}
26 changes: 13 additions & 13 deletions Dotnet.Samples.AspNetCore.WebApi.Tests/PlayerControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public async Task GivenPostAsync_WhenModelStateIsInvalid_ThenResponseStatusCodeS
var service = new Mock<IPlayerService>();
var logger = PlayerMocks.LoggerMock<PlayersController>();

var controller = new PlayersController(service.Object, logger);
var controller = new PlayersController(service.Object, logger.Object);
controller.ModelState.Merge(PlayerStubs.CreateModelError("FirstName", "Required"));

// Act
Expand All @@ -46,7 +46,7 @@ public async Task GivenPostAsync_WhenServiceRetrieveByIdAsyncReturnsPlayer_ThenR
service.Setup(service => service.RetrieveByIdAsync(It.IsAny<long>())).ReturnsAsync(player);
var logger = PlayerMocks.LoggerMock<PlayersController>();

var controller = new PlayersController(service.Object, logger);
var controller = new PlayersController(service.Object, logger.Object);

// Act
var result = await controller.PostAsync(player);
Expand All @@ -72,9 +72,9 @@ public async Task GivenPostAsync_WhenServiceRetrieveByIdAsyncReturnsNull_ThenRes
service.Setup(service => service.CreateAsync(It.IsAny<Player>()));
var logger = PlayerMocks.LoggerMock<PlayersController>();

var controller = new PlayersController(service.Object, logger)
var controller = new PlayersController(service.Object, logger.Object)
{
Url = PlayerMocks.UrlHelperMock()
Url = PlayerMocks.UrlHelperMock().Object,
};

// Act
Expand Down Expand Up @@ -102,7 +102,7 @@ public async Task GivenGetAsync_WhenServiceRetrieveAsyncReturnsListOfPlayers_The
service.Setup(service => service.RetrieveAsync()).ReturnsAsync(players);
var logger = PlayerMocks.LoggerMock<PlayersController>();

var controller = new PlayersController(service.Object, logger);
var controller = new PlayersController(service.Object, logger.Object);

// Act
var result = await controller.GetAsync();
Expand All @@ -126,7 +126,7 @@ public async Task GivenGetAsync_WhenServiceRetrieveAsyncReturnsEmptyList_ThenRes
service.Setup(service => service.RetrieveAsync()).ReturnsAsync(players);
var logger = PlayerMocks.LoggerMock<PlayersController>();

var controller = new PlayersController(service.Object, logger);
var controller = new PlayersController(service.Object, logger.Object);

// Act
var result = await controller.GetAsync();
Expand All @@ -149,7 +149,7 @@ public async Task GivenGetByIdAsync_WhenServiceRetrieveByIdAsyncReturnsNull_Then
.ReturnsAsync(null as Player);
var logger = PlayerMocks.LoggerMock<PlayersController>();

var controller = new PlayersController(service.Object, logger);
var controller = new PlayersController(service.Object, logger.Object);

// Act
var result = await controller.GetByIdAsync(It.IsAny<long>());
Expand All @@ -171,7 +171,7 @@ public async Task GivenGetByIdAsync_WhenServiceRetrieveByIdAsyncReturnsPlayer_Th
service.Setup(service => service.RetrieveByIdAsync(It.IsAny<long>())).ReturnsAsync(player);
var logger = PlayerMocks.LoggerMock<PlayersController>();

var controller = new PlayersController(service.Object, logger);
var controller = new PlayersController(service.Object, logger.Object);

// Act
var result = await controller.GetByIdAsync(It.IsAny<long>());
Expand All @@ -197,7 +197,7 @@ public async Task GivenPutAsync_WhenModelStateIsInvalid_ThenResponseStatusCodeSh
var service = new Mock<IPlayerService>();
var logger = PlayerMocks.LoggerMock<PlayersController>();

var controller = new PlayersController(service.Object, logger);
var controller = new PlayersController(service.Object, logger.Object);
controller.ModelState.Merge(PlayerStubs.CreateModelError("FirstName", "Required"));

// Act
Expand All @@ -220,7 +220,7 @@ public async Task GivenPutAsync_WhenServiceRetrieveByIdAsyncReturnsNull_ThenResp
.ReturnsAsync(null as Player);
var logger = PlayerMocks.LoggerMock<PlayersController>();

var controller = new PlayersController(service.Object, logger);
var controller = new PlayersController(service.Object, logger.Object);

// Act
var result = await controller.PutAsync(It.IsAny<long>(), It.IsAny<Player>());
Expand All @@ -244,7 +244,7 @@ public async Task GivenPutAsync_WhenServiceRetrieveByIdAsyncReturnsPlayer_ThenRe
service.Setup(service => service.UpdateAsync(It.IsAny<Player>()));
var logger = PlayerMocks.LoggerMock<PlayersController>();

var controller = new PlayersController(service.Object, logger);
var controller = new PlayersController(service.Object, logger.Object);

// Act
var result = await controller.PutAsync(id, player);
Expand Down Expand Up @@ -272,7 +272,7 @@ public async Task GivenDeleteAsync_WhenServiceRetrieveByIdAsyncReturnsNull_ThenR
.ReturnsAsync(null as Player);
var logger = PlayerMocks.LoggerMock<PlayersController>();

var controller = new PlayersController(service.Object, logger);
var controller = new PlayersController(service.Object, logger.Object);

// Act
var result = await controller.DeleteAsync(It.IsAny<long>());
Expand All @@ -295,7 +295,7 @@ public async Task GivenDeleteAsync_WhenServiceRetrieveByIdAsyncReturnsPlayer_The
service.Setup(service => service.DeleteAsync(It.IsAny<long>()));
var logger = PlayerMocks.LoggerMock<PlayersController>();

var controller = new PlayersController(service.Object, logger);
var controller = new PlayersController(service.Object, logger.Object);

// Act
var result = await controller.DeleteAsync(It.IsAny<long>());
Expand Down
27 changes: 19 additions & 8 deletions Dotnet.Samples.AspNetCore.WebApi.Tests/PlayerServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public PlayerServiceTests()
_context = PlayerStubs.CreateContext(_dbContextOptions);
PlayerStubs.CreateTable(_context);
PlayerStubs.SeedContext(_context);
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
}

public void Dispose()
Expand All @@ -31,52 +32,62 @@ public void Dispose()
}

[Fact]
[Trait("Category", "Retrieve")]
[Trait("Category", "RetrieveAsync")]
public async Task GivenRetrieveAsync_WhenInvoked_ThenShouldReturnAllPlayers()
{
// Arrange
var players = PlayerDataBuilder.SeedWithDeserializedJson();
var players = PlayerDataBuilder.SeedWithStarting11();
var logger = PlayerMocks.LoggerMock<PlayerService>();
var memoryCache = PlayerMocks.MemoryCacheMock(It.IsAny<object>());
var value = It.IsAny<object>();

var service = new PlayerService(_context, logger, memoryCache);
var service = new PlayerService(_context, logger.Object, memoryCache.Object);

// Act
var result = await service.RetrieveAsync();

// Assert
memoryCache.Verify(
cache => cache.TryGetValue(It.IsAny<object>(), out value),
Times.Exactly(1)
);
result.Should().BeEquivalentTo(players);
}

[Fact]
[Trait("Category", "Retrieve")]
[Trait("Category", "RetrieveAsync")]
public async Task GivenRetrieveAsync_WhenInvokedTwice_ThenSecondExecutionTimeShouldBeLessThanFirst()
{
// Arrange
var players = PlayerDataBuilder.SeedWithDeserializedJson();
var players = PlayerDataBuilder.SeedWithStarting11();
var logger = PlayerMocks.LoggerMock<PlayerService>();
var memoryCache = PlayerMocks.MemoryCacheMock(players);
var value = It.IsAny<object>();

var service = new PlayerService(_context, logger, memoryCache);
var service = new PlayerService(_context, logger.Object, memoryCache.Object);

// Act
var first = await ExecutionTimeAsync(() => service.RetrieveAsync());
var second = await ExecutionTimeAsync(() => service.RetrieveAsync());

// Assert
memoryCache.Verify(
cache => cache.TryGetValue(It.IsAny<object>(), out value),
Times.Exactly(2) // first + second
);
second.Should().BeLessThan(first);
}

[Fact]
[Trait("Category", "Retrieve")]
[Trait("Category", "RetrieveByIdAsync")]
public async Task GivenRetrieveByIdAsync_WhenInvokedWithPlayerId_ThenShouldReturnThePlayer()
{
// Arrange
var player = PlayerDataBuilder.SeedOneById(10);
var logger = PlayerMocks.LoggerMock<PlayerService>();
var memoryCache = PlayerMocks.MemoryCacheMock(It.IsAny<object>());

var service = new PlayerService(_context, logger, memoryCache);
var service = new PlayerService(_context, logger.Object, memoryCache.Object);

// Act
var result = await service.RetrieveByIdAsync(10);
Expand Down
23 changes: 12 additions & 11 deletions Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/PlayerMocks.cs
Original file line number Diff line number Diff line change
@@ -1,43 +1,44 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Moq;

namespace Dotnet.Samples.AspNetCore.WebApi.Tests
{
public static class PlayerMocks
{
public static ILogger<T> LoggerMock<T>()
public static Mock<ILogger<T>> LoggerMock<T>()
where T : class
{
var mock = new Mock<ILogger<T>>();

return mock.Object;
return new Mock<ILogger<T>>();
}

public static IMemoryCache MemoryCacheMock(object? value)
public static Mock<IMemoryCache> MemoryCacheMock(object? value)
{
var fromCache = false;
var mock = new Mock<IMemoryCache>();
mock.Setup(x => x.TryGetValue(It.IsAny<object>(), out value))
mock.Setup(cache => cache.TryGetValue(It.IsAny<object>(), out value))
.Returns(() =>
{
bool hasValue = fromCache;
fromCache = true; // Subsequent invocations will return true
return hasValue;
});
mock.Setup(x => x.CreateEntry(It.IsAny<object>())).Returns(Mock.Of<ICacheEntry>);
mock.Setup(cache => cache.CreateEntry(It.IsAny<object>()))
.Returns(Mock.Of<ICacheEntry>);
mock.Setup(cache => cache.Remove(It.IsAny<object>()));

return mock.Object;
return mock;
}

public static IUrlHelper UrlHelperMock()
public static Mock<IUrlHelper> UrlHelperMock()
{
var mock = new Mock<IUrlHelper>();
mock.Setup(u => u.Action(It.IsAny<UrlActionContext>())).Returns(It.IsAny<string>());
mock.Setup(url => url.Action(It.IsAny<UrlActionContext>())).Returns(It.IsAny<string>());

return mock.Object;
return mock;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static PlayerContext CreateContext(DbContextOptions<PlayerContext> dbCont

public static void SeedContext(PlayerContext context)
{
context.AddRange(PlayerDataBuilder.SeedWithDeserializedJson());
context.AddRange(PlayerDataBuilder.SeedWithStarting11());
context.SaveChanges();
}

Expand Down
Binary file modified Dotnet.Samples.AspNetCore.WebApi/Data/players-sqlite3.db
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
</ItemGroup>

<ItemGroup>
<Folder Include="Controllers/" />
<Content Include="Data/players-sqlite3.db">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
</Content>
</ItemGroup>

</Project>
5 changes: 4 additions & 1 deletion Dotnet.Samples.AspNetCore.WebApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@

builder.Services.AddControllers();

var dataSource =
$"{AppDomain.CurrentDomain.SetupInformation.ApplicationBase}/Data/players-sqlite3.db";

builder.Services.AddDbContext<PlayerContext>(options =>
options.UseSqlite(@"Data Source=Data/players-sqlite3.db")
options.UseSqlite($"Data Source={dataSource}")
);

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
Expand Down
Loading

0 comments on commit 471dc0e

Please sign in to comment.