Une gestion d’erreur de son API .Net automatique via ses exceptions
...
Avant .Net 8, tester du code qui utilise DateTime.Now() n’était pas trivial, on devait faire en sorte de mocker la Clock dans nos tests. Depuis .Net 8, c’est beaucoup plus facile grâce à TimeProvider
inclut par défaut !
En deux mots, TimeProvider
est une classe abstraite et on va pouvoir injecter dans nos tests une implémentation “Fake” qui va nous permettre de changer la date actuelle pendant leur exécution.
Exemple concret:
Voici une classe dans laquelle est injecté TimeProvider
(la configuration de l’injection de dépendance via le Program.cs est décrite plus bas)
public ComputeUserResourceCommandHandler(IResourceRepository resourceRepository, TimeProvider timeProvider) { _resourceRepository = resourceRepository; _timeProvider = timeProvider; }
Pour le contexte, on est dans un jeu ou les ressources sont calculées suivant (entre autre) le temps écoulé depuis la dernière mise à jour.
On n’utilise pas DateTime.Now()
qui est difficile à tester mais _timeProvider.GetUtcNow()
à la place.
public async Task Handle(ComputeUserResourceCommand command, CancellationToken cancellationToken) { var userResources = await _resourceRepository.GetResourceForUser(command.UserId); var elapsedTime = GetElapsedTime(userResources.LastUpdate); userResources.Food += GetAmountOfResourceToAdd(foodCoefficient, elapsedTime); userResources.Wood += GetAmountOfResourceToAdd(woodCoefficient, elapsedTime); userResources.Stone += GetAmountOfResourceToAdd(stoneCoefficient, elapsedTime); userResources.Gold += GetAmountOfResourceToAdd(goldCoefficient, elapsedTime); userResources.LastUpdate = _timeProvider.GetUtcNow().DateTime; } private long GetElapsedTime(DateTime userResourcesLastUpdate) { var lastUpdateTimestamp = new DateTimeOffset(userResourcesLastUpdate).ToUnixTimeSeconds(); var currentTimeStamp = _timeProvider.GetUtcNow().ToUnixTimeSeconds(); return _timeProvider.GetElapsedTime(lastUpdateTimestamp, currentTimeStamp).Ticks; }
Pour le tester, on n’a qu’à injecter un fake qui étend TimeProvider
et ça tombe bien, Microsoft en propose un déjà tout fait nommé FakeTimeProvider via la lib: Microsoft.Extensions.TimeProvider.Testing.
Il suffit d’installer le Nugget pour pouvoir nous en servir. C’est ce que j’ai fait dans l’exemple.
Voici le test associé:
Scenario: I gain resources over time Given the current date is 2024-11-18 10:14:00 And I am logged in And I have the following buildings | Farm | Sawmill | Gold mine | Stone quarry | | 1 | 1 | 1 | 1 | When the current date is 2024-11-18 11:04:00 Then my resources are | gold | food | wood | stone | | 52 | 185 | 206 | 204 |
On injecte le FakeTimeProvider
dans son fichier de StepDefinitions
public ResourceStepDefinitions(StrategyGameDbContext strategyGameDbContext, FakeTimeProvider timeProvider) { _strategyGameDbContext = strategyGameDbContext; _timeProvider = timeProvider; }
et on a une méthode sur-mesure permettant de spécifier la date actuelle sans avoir rien besoin de coder:
[Given(@"the current date is (.*)")] public void GivenTheCurrentDateIs(DateTime date) { _timeProvider.SetUtcNow(date); }
Il suffit d’’avoir le’utiliser FakeTimeProvider
au lieu de l’implémentation réelle pour ses tests et le tour est joué.
Dans notre Program.cs:
builder.Services.AddSingleton<TimeProvider>(sp => builder.Environment.IsEnvironment("Test") ? new FakeTimeProvider() : TimeProvider.System);
Et c’est tout, à partir de maintenant vous pouvez setter la date dans vos tests grâce à la méthode SetUtcNow
sans aucun code compliqué 🙂
On va voir comment avoir en quelques minutes des assertions qui vont vérifier les endpoints de notre API avec des scénarios de ce genre: Feature: Create a new account As a visitor, I can create an account to access the game Scenario: A visitor c...
Depuis .NET 9, le le support d’OpenAPI est directement inclus dans .NET et ne passe plus par les librairies Swagger par défaut (plus d’info sur ce choix ici si jamais ça vous intéresse). De façons simplifiée, la librairie Swashbuckle.AspNetCore.Sw...
Description du problème Par défaut, il n’est pas autorisé de faire des requêtes entre une application qui est dans un domaine A vers une autre qui serait dans un domaine B (pour des raisons de sécurité, il y a plus de détails dans les sources). S...