Derniers tips / Mémos
C# 15 : union types & closed hierarchies
Modélise nativement « exactement l'un de ces types », avec un switch exhaustif vérifié par le compilateur.
.NET 11 : runtime async
Fin des state machines générées par le compilateur : moins d'allocations et des stack traces enfin lisibles.
EF Core 11 : recherche vectorielle
Recherche sémantique directement dans SQL Server 2025, sans base vectorielle séparée, en LINQ typé.
Avalonia UI
Framework UI cross-platform pour .NET : Windows, macOS, Linux, iOS, Android et WebAssembly.
API Zip Async .NET 10
Découvrez les nouvelles APIs asynchrones pour la compression ZIP et les améliorations de performance dans .NET 10
NSubstitute : framework de mocking pour .NET
NSubstitute permet de créer facilement des objets mock pour vos tests unitaires avec une syntaxe fluide.
Notions de base
Substitution
var mock = Substitute.For<IFoo>();Retour simple
mock.Bar().Returns(42);Valeur calculée
mock.Add(Arg.Any<int>(), Arg.Any<int>()).Returns(callInfo => callInfo.Arg<int>(0) + callInfo.Arg<int>(1));Exceptions
mock.DoSomething().Throws(new InvalidOperationException());Vérification d’appel
mock.Received().Bar();
mock.DidNotReceive().Baz();Arguments spéciaux
Arg.Is<string>(s => s.StartsWith("Hello"));Partial mock
var partial = Substitute.ForPartsOf<MyConcreteClass>();Capturer les arguments
int captured;
// Simple variant (with When/Do)
mock.When(x => x.Do(Arg.Any<int>())).Do(call => captured = call.Arg<int>());
// (then execute mock.Do(123); so that captured = 123)Exemple complet
Le code à tester
public interface ICalculator
{
int Add(int a, int b);
void Log(string message);
}
public class MathService
{
private readonly ICalculator _calculator;
public MathService(ICalculator calculator)
{
_calculator = calculator;
}
public int ComputeSumAndLog(int x, int y)
{
var result = _calculator.Add(x, y);
_calculator.Log($"Result is {result}");
return result;
}
}Le test avec NSubstitute
using Xunit;
using NSubstitute;
public class MathServiceTests
{
[Fact]
public void ComputeSumAndLog_ReturnsSum_LogsMessage()
{
// Arrange: create a mock of ICalculator
var calc = Substitute.For<ICalculator>();
// Force Add() to return 5 when passed 2 and 3
calc.Add(2, 3).Returns(5);
// Prepare the service with the mock
var sut = new MathService(calc);
// Act: call the tested method
int result = sut.ComputeSumAndLog(2, 3);
// Assert: expected result
Assert.Equal(5, result);
// Assert: verify Log was called with the expected message
calc.Received().Log("Result is 5");
}
}Remarques
- <code>calc.Add(2,3).Returns(5)</code> force la valeur de retour uniquement pour ces arguments.
- Si vous ne précisez pas de retour explicite, le mock renverra la valeur par défaut du type (<code>0</code> pour <code>int</code>, <code>null</code> pour les références, etc.).
Cas pratiques supplémentaires
Mock d’une méthode asynchrone
public interface IAsyncService
{
Task<string> GetDataAsync(string key);
}
var asyncMock = Substitute.For<IAsyncService>();
asyncMock.GetDataAsync("abc").Returns(Task.FromResult("mocked data"));Mock d’une méthode qui lance une exception
calc.DoSomething().Throws(new ArgumentException());Vérifier le nombre d’appels
// Au moins deux appels
calc.Received(2).Log(Arg.Any<string>());
// Aucun appel
calc.DidNotReceive().Add(Arg.Any<int>(), Arg.Any<int>());Utiliser When pour capturer l’argument passé
int captured = -1;
calc.Add(Arg.Any<int>(), Arg.Any<int>())
.Returns(x => x.Arg<int>(0) + x.Arg<int>(1));
calc.When(call => call.Log(Arg.Any<string>()))
.Do(callInfo =>
{
var msg = callInfo.GetArguments()[0] as string;
// extract the number from the message
captured = int.Parse(msg.Split(' ')[2]);
});Bonnes pratiques
- Noms explicites — exemple lisible comme un pseudo‑code :
calc.Add(Arg.Any<int>(), Arg.Any<int>()).Returns(call => ...) - Évite les mocks inutiles — si vous n’interagissez pas avec l’objet, ne le mockez pas.
- Utilise Substitute.ForPartsOf<T>() — pour tester une classe concrète partiellement — vous gardez la logique interne et vous ne stubb ez que le nécessaire.
- Ne cache jamais un bug — si vous stubbez tout, un test peut passer même si le code est incorrect.
Sources : NSubstitute (GitHub)
Écrit le 2025-08-12