Unit testing private methods, should you make them public violating O (open/close) in SOLID or use a framework like Moq to to test the private methods via the public ones?
I'm trying to figure the best way to unit test GetHumanPlayerShot and CreateComputerShot private methods in the code sample below. They don't need to be public, so making them so would break open / closed principle in SOLID.
Whats the best way to unit test private methods? is there a way to check they are called with a unit test and check the result? I can mock out RandomLocationGenerator.Generate(0,10) as that implements an interface.
For brevity, ive omitted some code / classes in my example code
The only public method that needs to be exposed is RunGame as it returns the gameState.
Should i make methods public to make them testable or is there a better way?
public interface IBattleShipGameStateMachine { GameState RunGame(); } public class BattleShipGameStateMachine : IBattleShipGameStateMachine { public BattleShipGameStateMachine(IGame game, IConsole consoleWrapper, IRandomNumberGenerator randomLocationGenerator) { this.ConsoleWrapper = consoleWrapper; this.Game = game; this.RandomLocationGenerator = randomLocationGenerator; this.ValidShotTester = new Regex(@"\d+,\d+", RegexOptions.Compiled); this.gameState = GameState.NotStarted; } private GameState gameState { get; set; } public GameState RunGame() { this.gameState = GameState.GameRunning; IPlayer playerOne = this.Game.Players.First(p => p.PlayerNumber == 1); IPlayer playerTwo = this.Game.Players.First(p => p.PlayerNumber == 2); while (!this.gameState.Equals(GameState.PlayerOneWon) && !this.gameState.Equals(GameState.PlayerTwoWon)) { // human and computer take turns shooting Location shot = this.GetHumanPlayerShot(); if (this.gameState.Equals(GameState.PlayerExit)) { break; } // logic omitted here to check if ships hit / all ships sunk etc Location shot = CreateComputerShot(); } return this.gameState; } // creates a location x,y. need to unit test this to ensure its a valid location private Location CreateComputerShot() { int x = this.RandomLocationGenerator.Generate(0, 10); int y = this.RandomLocationGenerator.Generate(0, 10); Location shot = new Location(x, y); while (otherPlayer.Board.Coordinates[shot] == CoordinateState.ShotHit || otherPlayer.Board.Coordinates[shot] == CoordinateState.ShotMissed) { x = this.RandomLocationGenerator.Generate(0, 10); y = this.RandomLocationGenerator.Generate(0, 10); shot = new Location(x, y); } return shot; } private Location GetHumanPlayerShot() { while (true) { this.ConsoleWrapper.WriteLine("Enter x,y coordinates e.g. 1,2 or x to quit"); string shotCommand = this.ConsoleWrapper.ReadLine(); if (shotCommand.Equals("x", StringComparison.InvariantCultureIgnoreCase)) { this.gameState = GameState.PlayerExit; return new Location(0, 0); } Match match = this.ValidShotTester.Match(shotCommand); if (match.Success) { string[] playerCoordinates = shotCommand.Split(","); int x = Convert.ToInt32(playerCoordinates[0]); int y = Convert.ToInt32(playerCoordinates[1]); if (x > this.Game.BoardSize || y > this.Game.BoardSize) { this.ConsoleWrapper.WriteLine("Invalid shot try again"); continue; } Location shot = new Location(x, y); return shot; } this.ConsoleWrapper.WriteLine("Invalid shot try again"); } } } public struct Location { public Location(int x, int y) { this.X = x; this.Y = y; } public int X { get; set; } public int Y { get; set; } } public enum GameState { [Description("GameRunning")] GameRunning, [Description("NotStarted")] NotStarted, [Description("Exit Game")] PlayerExit, [Description("Player One Won!")] PlayerOneWon, [Description("Player Two Won!")] PlayerTwoWon }
0 comments:
Post a Comment