diff --git a/Correction/MiniJeuxFinal/GameFactory/PierrePapierCiseauxGameFactory.cs b/Correction/MiniJeuxFinal/GameFactory/PierrePapierCiseauxGameFactory.cs index 1df7cf1..1bac1e4 100644 --- a/Correction/MiniJeuxFinal/GameFactory/PierrePapierCiseauxGameFactory.cs +++ b/Correction/MiniJeuxFinal/GameFactory/PierrePapierCiseauxGameFactory.cs @@ -9,9 +9,8 @@ namespace MiniJeuxFinal.GameFactory { public static class PierrePapierCiseauxGameFactory { - public static PierrePapierCiseauxGame CreateGame() + public static PierrePapierCiseauxGame CreateGame(IConsole consoleService) { - var consoleService = new ConsoleService(); consoleService.WriteLine("Entrer votre nom de joueur:"); var userName = consoleService.ReadLine(); consoleService.WriteLine("Entrer le nombre de round:"); diff --git a/Correction/MiniJeuxFinal/Games/PierrePapierCiseaux/PierrePapierCiseauxGame.cs b/Correction/MiniJeuxFinal/Games/PierrePapierCiseaux/PierrePapierCiseauxGame.cs index 83d9b7d..8cc0a4a 100644 --- a/Correction/MiniJeuxFinal/Games/PierrePapierCiseaux/PierrePapierCiseauxGame.cs +++ b/Correction/MiniJeuxFinal/Games/PierrePapierCiseaux/PierrePapierCiseauxGame.cs @@ -51,7 +51,7 @@ namespace MiniJeuxFinal.Games.PierrePapierCiseaux var actionComputer = (IActionPpc)_computer.Play(); TurnStarted?.Invoke(this, (actionPLayer.Name, actionComputer.Name)); - if (actionPLayer.ToWin(actionComputer) && actionComputer.ToWin(actionPLayer)) + if (actionPLayer.ToWin(actionComputer) == actionComputer.ToWin(actionPLayer)) return null; if (actionPLayer.ToWin(actionComputer)) diff --git a/Correction/MiniJeuxFinal/Program.cs b/Correction/MiniJeuxFinal/Program.cs index 4512f35..5934095 100644 --- a/Correction/MiniJeuxFinal/Program.cs +++ b/Correction/MiniJeuxFinal/Program.cs @@ -1,11 +1,14 @@ using MiniJeuxFinal.GameFactory; using MiniJeuxFinal.Ui; +using MiniJeuxFinal.Wrappers; class Program() { public static void Main() { - var runner = new GameRunnerConsole(PierrePapierCiseauxGameFactory.CreateGame()); + var consoleService = new ConsoleService(); + var game = PierrePapierCiseauxGameFactory.CreateGame(consoleService); + var runner = new GameRunnerConsole(consoleService, game); runner.Run(); } } diff --git a/Correction/MiniJeuxFinal/Ui/GameRunnerConsole.cs b/Correction/MiniJeuxFinal/Ui/GameRunnerConsole.cs index 7ea189d..ed341ed 100644 --- a/Correction/MiniJeuxFinal/Ui/GameRunnerConsole.cs +++ b/Correction/MiniJeuxFinal/Ui/GameRunnerConsole.cs @@ -1,57 +1,68 @@ using MiniJeuxFinal.Games; +using MiniJeuxFinal.Wrappers; namespace MiniJeuxFinal.Ui { - public class GameRunnerConsole + public class GameRunnerConsole : IDisposable { private readonly IGame _game; + private readonly IConsole _console; - public GameRunnerConsole(IGame game) + + + public GameRunnerConsole(IConsole console, IGame game) { + _console = console; _game = game; - SetupEvents(); - } - - private void SetupEvents() - { _game.TurnStarted += OnTurnStarted; _game.TurnEnded += OnTurnEnded; _game.GameEnded += OnGameEnded; } + + + public void Dispose() + { + _game.TurnStarted -= OnTurnStarted; + _game.TurnEnded -= OnTurnEnded; + _game.GameEnded -= OnGameEnded; + } + + public void Run() + { + _console.WriteLine(_game.Name); + _console.WriteLine(); + _game.Start(); + } + + + private void OnTurnStarted(object? sender, (string playerChoice, string computerChoice) e) { - Console.WriteLine($"Tu joues : {e.playerChoice}"); - Console.WriteLine($"L'ordinateur joue : {e.computerChoice}"); + _console.WriteLine($"Tu joues : {e.playerChoice}"); + _console.WriteLine($"L'ordinateur joue : {e.computerChoice}"); } private void OnTurnEnded(object? sender, (IPlayer? winner, IPlayer[] players) e) { Thread.Sleep(2000); - Console.Clear(); + _console.Clear(); if (e.winner == null) - Console.WriteLine("Égalité !"); + _console.WriteLine("Égalité !"); else - Console.WriteLine($"{e.winner.Name} gagne la manche"); + _console.WriteLine($"{e.winner.Name} gagne la manche"); - Console.WriteLine(); - Console.WriteLine($"Score : {e.players[0]} - {e.players[1]}"); - Console.WriteLine(); + _console.WriteLine(); + _console.WriteLine($"Score : {e.players[0]} - {e.players[1]}"); + _console.WriteLine(); } private void OnGameEnded(object? sender, (IPlayer winner, IPlayer[] players) e) { - Console.WriteLine($"{e.winner.Name} A GAGNÉ ! 🎉🎉🎉"); + _console.WriteLine($"{e.winner.Name} A GAGNÉ ! 🎉🎉🎉"); var loser = e.players.First(p => p.Name != e.winner.Name); - Console.WriteLine($"Score final : {e.winner.Score} - {loser.Score}"); - } - - public void Run() - { - Console.WriteLine(_game.Name); - Console.WriteLine(); - _game.Start(); + _console.WriteLine($"Score final : {e.winner.Score} - {loser.Score}"); } } } diff --git a/Correction/MiniJeuxFinal/Wrappers/ConsoleService.cs b/Correction/MiniJeuxFinal/Wrappers/ConsoleService.cs index 599b64b..87147b2 100644 --- a/Correction/MiniJeuxFinal/Wrappers/ConsoleService.cs +++ b/Correction/MiniJeuxFinal/Wrappers/ConsoleService.cs @@ -2,9 +2,15 @@ { internal class ConsoleService : IConsole { + public void Clear() => + Console.Clear(); + public string? ReadLine() => Console.ReadLine(); + public void WriteLine() => + Console.WriteLine(); + public void WriteLine(string message) => Console.WriteLine(message); } diff --git a/Correction/MiniJeuxFinal/Wrappers/IConsole.cs b/Correction/MiniJeuxFinal/Wrappers/IConsole.cs index c177d08..6c71d58 100644 --- a/Correction/MiniJeuxFinal/Wrappers/IConsole.cs +++ b/Correction/MiniJeuxFinal/Wrappers/IConsole.cs @@ -4,6 +4,10 @@ { string? ReadLine(); + void Clear(); + + void WriteLine(); + void WriteLine(string message); } } diff --git a/Correction/ReadMe.md b/Correction/ReadMe.md index 80bff8f..2057a2e 100644 --- a/Correction/ReadMe.md +++ b/Correction/ReadMe.md @@ -1,3 +1,47 @@ -# Exemple final de se à quoi on pourrait aller +# Exemple final de ce à quoi on pourrait arriver -Cette solution est un aperçu des bonne pratique mise en place. \ No newline at end of file +Cette solution est un aperçu des bonnes pratiques mises en place. + +``` +MiniJeuxFinal/ +│ +├── MiniJeuxFinal.csproj # Fichier projet C# .NET 9 +│ +├── Program.cs # Point d'entrée de l'application +│ +├── GameFactory/ +│ └── PierrePapierCiseauxGameFactory.cs # Création du jeu +│ +├── Games/ +│ ├── IGame.cs # Interface générale d'un jeu +│ ├── IAction.cs # Interface générale d'une action +│ ├── IPlayer.cs # Interface générale d'un joueur +│ │ +│ ├── PierrePapierCiseaux/ # Implémentation spécifique du jeu PPC +│ │ ├── PierrePapierCiseauxGame.cs # Classe principale du jeu PPC +│ │ │ +│ │ ├── Actions/ # Actions spécifiques au PPC +│ │ │ ├── IActionPpc.cs # Interface spécialisée (hérite de IAction) +│ │ │ ├── StoneAction.cs # Pierre +│ │ │ ├── PaperAction.cs # Papier +│ │ │ └── ScissorsAction.cs # Ciseaux +│ │ │ +│ │ ├── Factories/ # Factories pour créer les actions PPC +│ │ │ ├── IActionPpcFactory.cs # Interface des factories PPC +│ │ │ ├── InputActionFactory.cs # Factory pour l'entrée utilisateur +│ │ │ └── RandomActionFactory.cs# Factory aléatoire (IA) +│ │ │ +│ │ └── Players/ +│ │ └── PlayerPpc.cs # Joueur spécialisé pour le PPC +│ │ +│ └── Trash/ # Anciennes versions ou brouillons +│ ├── Pendu.cs +│ └── Ppc.cs +│ +├── Ui/ +│ └── GameRunnerConsole.cs # Orchestrateur UI Console (affichage) +│ +├── Wrappers/ # Abstractions des dépendances externes +│ ├── IConsole.cs # Interface pour la console +│ └── ConsoleService.cs # Implémentation réelle de IConsole +``` \ No newline at end of file diff --git a/Correction/Tests/MiniJeuxFinal.Test/Games/PierrePapierCiseaux/PierrePapierCiseauxGameTests.cs b/Correction/Tests/MiniJeuxFinal.Test/Games/PierrePapierCiseaux/PierrePapierCiseauxGameTests.cs new file mode 100644 index 0000000..d041f65 --- /dev/null +++ b/Correction/Tests/MiniJeuxFinal.Test/Games/PierrePapierCiseaux/PierrePapierCiseauxGameTests.cs @@ -0,0 +1,221 @@ +using FakeItEasy; +using MiniJeuxFinal.Games; +using MiniJeuxFinal.Games.PierrePapierCiseaux; +using MiniJeuxFinal.Games.PierrePapierCiseaux.Actions; +using NFluent; +using System.Reflection; + +namespace MiniJeuxFinal.Test.Games.PierrePapierCiseaux +{ + public class PierrePapierCiseauxGameTests + { + private readonly IPlayer _fakePlayer; + private readonly IPlayer _fakeComputer; + private readonly PierrePapierCiseauxGame _target; + + public PierrePapierCiseauxGameTests() + { + _fakePlayer = A.Fake(); + _fakeComputer = A.Fake(); + _target = new PierrePapierCiseauxGame(totalRound: 3, _fakePlayer, _fakeComputer); + } + + + + + [Fact] + public void Name_Should_Return_Correct_Game_Name() + { + Check.That(_target.Name).IsEqualTo("Jeu du Pierre, Papier, Ciseaux 🧱 📄 ✂️"); + } + + [Fact] + public void Constructor_Should_Set_Properties_Correctly() + { + const int totalRounds = 5; + var player = A.Fake(); + var computer = A.Fake(); + + var game = new PierrePapierCiseauxGame(totalRounds, player, computer); + + Check.That(game).IsNotNull(); + } + + [Fact] + public void Start_Should_End_Game_When_Computer_Reaches_TotalRounds() + { + A.CallTo(() => _fakePlayer.Score).Returns(0); + A.CallTo(() => _fakeComputer.Score).ReturnsNextFromSequence(0, 1, 2, 3); // Atteint 3 + + var fakePlayerAction = A.Fake(); + var fakeComputerAction = A.Fake(); + + A.CallTo(() => fakePlayerAction.ToWin(fakeComputerAction)).Returns(false); + A.CallTo(() => fakeComputerAction.ToWin(fakePlayerAction)).Returns(true); + + A.CallTo(() => _fakePlayer.Play()).Returns(fakePlayerAction); + A.CallTo(() => _fakeComputer.Play()).Returns(fakeComputerAction); + + IPlayer? capturedWinner = null; + _target.GameEnded += (sender, args) => capturedWinner = args.winner; + + _target.Start(); + + Check.That(capturedWinner).IsEqualTo(_fakeComputer); + } + + [Theory] + [InlineData(true, false, true)] // Joueur gagne + [InlineData(false, true, false)] // Ordinateur gagne + [InlineData(false, false, null)] // Égalité (les deux faux) + public void Play_Should_Return_Correct_Winner(bool playerWins, + bool computerWins, + bool? expectedPlayerWins) + { + var fakePlayerAction = A.Fake(); + var fakeComputerAction = A.Fake(); + + A.CallTo(() => fakePlayerAction.Name).Returns("Pierre"); + A.CallTo(() => fakeComputerAction.Name).Returns("Ciseaux"); + + A.CallTo(() => fakePlayerAction.ToWin(fakeComputerAction)).Returns(playerWins); + A.CallTo(() => fakeComputerAction.ToWin(fakePlayerAction)).Returns(computerWins); + + A.CallTo(() => _fakePlayer.Play()).Returns(fakePlayerAction); + A.CallTo(() => _fakeComputer.Play()).Returns(fakeComputerAction); + + (string, string)? capturedChoices = null; + _target.TurnStarted += (sender, args) => capturedChoices = args; + + // Act - Appeler Play via réflexion car c'est privé + var playMethod = typeof(PierrePapierCiseauxGame) + .GetMethod("Play", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + var result = (IPlayer?)playMethod?.Invoke(_target, null); + + Check.That(capturedChoices?.Item1).IsEqualTo("Pierre"); + Check.That(capturedChoices?.Item2).IsEqualTo("Ciseaux"); + + if (expectedPlayerWins == true) + Check.That(result).IsEqualTo(_fakePlayer); + else if (expectedPlayerWins == false) + Check.That(result).IsEqualTo(_fakeComputer); + else + Check.That(result).IsNull(); + } + + [Fact] + public void Start_Should_Increment_Score_When_There_Is_A_Winner() + { + A.CallTo(() => _fakePlayer.Score).ReturnsNextFromSequence(0, 1, 2); // S'arrête à 2 + A.CallTo(() => _fakeComputer.Score).Returns(0); + + var fakePlayerAction = A.Fake(); + var fakeComputerAction = A.Fake(); + + A.CallTo(() => fakePlayerAction.ToWin(fakeComputerAction)).Returns(true); + A.CallTo(() => fakeComputerAction.ToWin(fakePlayerAction)).Returns(false); + + A.CallTo(() => _fakePlayer.Play()).Returns(fakePlayerAction); + A.CallTo(() => _fakeComputer.Play()).Returns(fakeComputerAction); + + IPlayer? turnWinner = null; + _target.TurnEnded += (sender, args) => turnWinner = args.winner; + + A.CallTo(() => _fakePlayer.Score).Returns(0); + A.CallTo(() => _fakeComputer.Score).Returns(0); + + A.CallTo(() => _fakePlayer.IncrementScore()) + .Invokes(() => A.CallTo(() => _fakePlayer.Score).Returns(1)); + + // Exécuter un seul tour + var playMethod = typeof(PierrePapierCiseauxGame) + .GetMethod("Play", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + var winner = (IPlayer?)playMethod?.Invoke(_target, null); + + if (winner != null) + winner.IncrementScore(); + + Check.That(winner).IsEqualTo(_fakePlayer); + A.CallTo(() => _fakePlayer.IncrementScore()).MustHaveHappenedOnceExactly(); + } + + [Fact] + public void Start_Should_Not_Increment_Score_On_Draw() + { + var fakePlayerAction = A.Fake(); + var fakeComputerAction = A.Fake(); + + // Égalité : les deux retournent false + A.CallTo(() => fakePlayerAction.ToWin(fakeComputerAction)).Returns(false); + A.CallTo(() => fakeComputerAction.ToWin(fakePlayerAction)).Returns(false); + + A.CallTo(() => _fakePlayer.Play()).Returns(fakePlayerAction); + A.CallTo(() => _fakeComputer.Play()).Returns(fakeComputerAction); + + var playMethod = typeof(PierrePapierCiseauxGame) + .GetMethod("Play", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + var winner = (IPlayer?)playMethod?.Invoke(_target, null); + + Check.That(winner).IsNull(); + A.CallTo(() => _fakePlayer.IncrementScore()).MustNotHaveHappened(); + A.CallTo(() => _fakeComputer.IncrementScore()).MustNotHaveHappened(); + } + + [Fact] + public void Start_Should_Raise_TurnEnded_After_Each_Round() + { + A.CallTo(() => _fakePlayer.Score).ReturnsNextFromSequence(0, 1, 2, 3); // 3 tours + A.CallTo(() => _fakeComputer.Score).Returns(0); + + var fakeAction = A.Fake(); + A.CallTo(() => fakeAction.ToWin(A._)).Returns(true); // Joueur gagne toujours + + A.CallTo(() => _fakePlayer.Play()).Returns(fakeAction); + A.CallTo(() => _fakeComputer.Play()).Returns(fakeAction); + + var turnEndedCount = 0; + _target.TurnEnded += (sender, args) => turnEndedCount++; + + _target.Start(); + + Check.That(turnEndedCount).IsEqualTo(3); + } + + [Fact] + public void Start_Should_Raise_TurnStarted_Before_Each_Round() + { + A.CallTo(() => _fakePlayer.Score).ReturnsNextFromSequence(0, 1, 2, 3); + A.CallTo(() => _fakeComputer.Score).Returns(0); + + var fakeAction = A.Fake(); + A.CallTo(() => fakeAction.Name).Returns("TestAction"); + + A.CallTo(() => _fakePlayer.Play()).Returns(fakeAction); + A.CallTo(() => _fakeComputer.Play()).Returns(fakeAction); + + var turnStartedCount = 0; + _target.TurnStarted += (sender, args) => turnStartedCount++; + + _target.Start(); + + Check.That(turnStartedCount).IsEqualTo(3); + } + + [Fact] + public void Play_Should_Throw_When_Action_Is_Not_IActionPpc() + { + var invalidAction = A.Fake(); // Pas IActionPpc + A.CallTo(() => _fakePlayer.Play()).Returns(invalidAction); + A.CallTo(() => _fakeComputer.Play()).Returns(A.Fake()); + + var playMethod = typeof(PierrePapierCiseauxGame) + .GetMethod("Play", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + Check.ThatCode(() => playMethod?.Invoke(_target, null)) + .Throws(); + } + } +} diff --git a/Correction/Tests/MiniJeuxFinal.Test/PpcTest.cs b/Correction/Tests/MiniJeuxFinal.Test/PpcTest.cs deleted file mode 100644 index be21724..0000000 --- a/Correction/Tests/MiniJeuxFinal.Test/PpcTest.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NFluent; - -namespace MiniJeuxFinal.Test; - -public class PpcTest -{ - [Fact] - public void Test1() - { - var i = 1; - Check.That(i).IsEqualTo(1); - } -} diff --git a/Correction/Tests/MiniJeuxFinal.Test/Ui/GameRunnerConsoleTests.cs b/Correction/Tests/MiniJeuxFinal.Test/Ui/GameRunnerConsoleTests.cs new file mode 100644 index 0000000..ef33486 --- /dev/null +++ b/Correction/Tests/MiniJeuxFinal.Test/Ui/GameRunnerConsoleTests.cs @@ -0,0 +1,157 @@ +using FakeItEasy; +using MiniJeuxFinal.Games; +using MiniJeuxFinal.Ui; +using MiniJeuxFinal.Wrappers; +using NFluent; + +namespace MiniJeuxFinal.Test.Ui +{ + public class GameRunnerConsoleTests + { + private readonly IGame _fakeGame; + private readonly IConsole _fakeConsole; + private readonly GameRunnerConsole _target; + + public GameRunnerConsoleTests() + { + _fakeGame = A.Fake(); + _fakeConsole = A.Fake(); + _target = new GameRunnerConsole(_fakeConsole, _fakeGame); + } + + // Dispose pour nettoyer les tests + public void Dispose() => _target.Dispose(); + + + + [Fact] + public void Constructor_Should_Subscribe_To_Game_Events() + { + A.CallTo(_fakeGame) + .Where(call => call.Method.Name == "add_TurnStarted") + .MustHaveHappenedOnceExactly(); + + A.CallTo(_fakeGame) + .Where(call => call.Method.Name == "add_TurnEnded") + .MustHaveHappenedOnceExactly(); + + A.CallTo(_fakeGame) + .Where(call => call.Method.Name == "add_GameEnded") + .MustHaveHappenedOnceExactly(); + } + + [Fact] + public void Dispose_Should_Unsubscribe_From_Game_Events() + { + _target.Dispose(); + + A.CallTo(_fakeGame) + .Where(call => call.Method.Name == "remove_TurnStarted") + .MustHaveHappenedOnceExactly(); + + A.CallTo(_fakeGame) + .Where(call => call.Method.Name == "remove_TurnEnded") + .MustHaveHappenedOnceExactly(); + + A.CallTo(_fakeGame) + .Where(call => call.Method.Name == "remove_GameEnded") + .MustHaveHappenedOnceExactly(); + } + + [Fact] + public void Run_Should_Display_GameName_Then_Start_Game() + { + const string gameName = "PIERRE-PAPIER-CISEAUX"; + A.CallTo(() => _fakeGame.Name).Returns(gameName); + _target.Run(); + + A.CallTo(() => _fakeConsole.WriteLine(gameName)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _fakeConsole.WriteLine()).MustHaveHappenedOnceExactly(); + A.CallTo(() => _fakeGame.Start()).MustHaveHappenedOnceExactly(); + + var calls = Fake.GetCalls(_fakeConsole).ToList(); + Check.That(calls[0].Arguments[0]).IsEqualTo(gameName); + Check.That(calls[1].Arguments).IsEmpty(); // WriteLine() sans paramètre + } + + [Fact] + public void OnTurnStarted_Should_Display_Both_Choices() + { + _fakeGame.TurnStarted += Raise.FreeForm.With(null, ("1", "2")); + + A.CallTo(() => _fakeConsole.WriteLine($"Tu joues : 1")) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _fakeConsole.WriteLine($"L'ordinateur joue : 2")) + .MustHaveHappenedOnceExactly(); + } + + [Theory] + [InlineData(null, "Égalité !")] // winner = null + [InlineData("Joueur1", "Joueur1 gagne la manche")] // winner = Joueur1 + public void OnTurnEnded_Should_Clear_And_Display_Results(string? winnerName, string expectedMessage) + { + // Arrange + IPlayer? fakeWinner = winnerName != null ? A.Fake() : null; + if (fakeWinner != null) + A.CallTo(() => fakeWinner.Name).Returns(winnerName); + + var fakePlayer1 = A.Fake(); + var fakePlayer2 = A.Fake(); + A.CallTo(() => fakePlayer1.ToString()).Returns("Joueur1 (3 pts)"); + A.CallTo(() => fakePlayer2.ToString()).Returns("Ordinateur (1 pt)"); + + var players = new[] { fakePlayer1, fakePlayer2 }; + var eventArgs = (fakeWinner, players); + + // Capturer le handler + EventHandler<(IPlayer?, IPlayer[])>? handler = null; + A.CallTo(_fakeGame) + .Where(call => call.Method.Name == "add_TurnEnded") + .Invokes(call => handler = (EventHandler<(IPlayer?, IPlayer[])>)call.Arguments[0]); + + var target2 = new GameRunnerConsole(_fakeConsole, _fakeGame); + + handler?.Invoke(this, eventArgs); + + A.CallTo(() => _fakeConsole.Clear()) + .MustHaveHappenedOnceExactly() + .Then(A.CallTo(() => _fakeConsole.WriteLine(expectedMessage)) + .MustHaveHappenedOnceExactly()) + .Then(A.CallTo(() => _fakeConsole.WriteLine(A.That.Contains("Score :"))) + .MustHaveHappenedOnceExactly()); + + target2.Dispose(); + } + + [Fact] + public void OnGameEnded_Should_Display_Winner_And_Final_Score() + { + var fakeWinner = A.Fake(); + var fakeLoser = A.Fake(); + + A.CallTo(() => fakeWinner.Name).Returns("Joueur1"); + A.CallTo(() => fakeWinner.Score).Returns(5); + A.CallTo(() => fakeLoser.Name).Returns("Ordinateur"); + A.CallTo(() => fakeLoser.Score).Returns(2); + + var players = new[] { fakeWinner, fakeLoser }; + var eventArgs = (fakeWinner, players); + + EventHandler<(IPlayer, IPlayer[])>? handler = null; + A.CallTo(_fakeGame) + .Where(call => call.Method.Name == "add_GameEnded") + .Invokes(call => handler = (EventHandler<(IPlayer, IPlayer[])>)call.Arguments[0]); + + var target2 = new GameRunnerConsole(_fakeConsole, _fakeGame); + + handler?.Invoke(this, eventArgs); + + A.CallTo(() => _fakeConsole.WriteLine("Joueur1 A GAGNÉ ! 🎉🎉🎉")) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _fakeConsole.WriteLine("Score final : 5 - 2")) + .MustHaveHappenedOnceExactly(); + + target2.Dispose(); + } + } +}