Unexpected Input

While replacing calls to Console with calls to our injected class, we found two use cases that we did not plan for. The first use case is fairly involved and has a couple parameters we need to handle:

Console.Write("+", c = c + 1);

The second use case is more simple and doesn't take any parameters:

Console.WriteLine();

The second use case is the easiest to deal with, so let us write a quick test for that now.

In the file WriteLineTests.cs:

[Fact]
public void ItCanWriteABlankLine()
{
// Arrange
var inout = new MockInputOutput();

// Act
inout.WriteLine();

// Assert
Assert.Single(inout.OutFeed);
Assert.Equal(Environment.NewLine, inout.OutFeed.First());
}

In the file IInputOutput.cs:

void WriteLine(string text = null);

In the file MockInputOutput.cs:

public void WriteLine(string text = null)
{
OutFeed.Add((text ?? "") + Environment.NewLine);
}

The next issue is slightly more complicated. If we want to handle it accurately, we will need to do quite a bit of regular expression and string manipulation. However, we don't need it to be "correct"; we only need it to work as expected by the application. In the singular case where this is being used, the value that should be placed into the string being written, isn't. The original developer abused the functionality of Console.Write to reduce the number of lines in the if statement so they could avoid brackets. So, all we need to do for the code to continue to work is allow for the input to take place. A simple interface extension should provide that for us.

In the file IInputOutput.cs:

void Write(string text, params object[] args);

In the file MockInputOutput.cs:

public void Write(string text, params object[] args)
{
OutFeed.Add(text);
}

Back in the application code, we can finish making our changes. Here is the updated application.

In the file ConsoleInputOutput.cs:

public class ConsoleInputOutput : IInputOutput
{
public void Write(string text, params object[] args)
{
Console.Write(text, args);
}

public void WriteLine(string text)
{
Console.WriteLine(text);
}

public char Read()
{
return Console.ReadKey().KeyChar;
}

public string ReadLine()
{
return Console.ReadLine();
}
}

In the file Program.cs:

class Program
{
static void Main(string[] args)
{
var inout = new ConsoleInputOutput();
var game = new Mastermind(inout);
game.Play(args);
}
}

In the file Mastermind.cs:

public class Mastermind
{
private readonly IInputOutput _inout;

public Mastermind(IInputOutput inout)
{
_inout = inout;
}

public void Play(string[] args)
{
// Variable Declarations - Global??
char[] g;
char[] p = new[] { 'A', 'A', 'A', 'A' };
int i = 0;
int j = 0;
int x = 0;
int c = 0;

// Initialize randomness
Random rand = new Random(DateTime.Now.Millisecond);

// Determine if a password was passed in?
if (args.Length > 0 && args[0] != null) p = args[0].ToCharArray();
else goto randomize_password; // Create a password if one was not
provided
// Player move - guess the password
guess: _inout.Write("Take a guess: ");
g = _inout.ReadLine().ToArray();
i = i + 1;
if (g.Length != 4) goto wrong_size;
if (g == p) goto success;
x = 0;
c = 0;

// Check if the password provided by the player is correct
check_loop:
if (g[x] > 65 + 26) g[x] = (char)(g[x] - 32);
if (g[x] == p[x]) _inout.Write("+", c = c + 1);
else if (p.Contains(g[x])) _inout.Write("-");
x = x + 1;
if (x < 4) goto check_loop; // Still checking??
_inout.WriteLine();
if (c == 4) goto success; // Password must have been correct
goto guess; // No correct, try again

// Game over you win
success: _inout.WriteLine("Congratulations you guessed the password
in " + i + " tries.");
goto end;
// Password guess was wrong size - Error Message
wrong_size: _inout.WriteLine("Password length is 4.");
goto guess;

// Create a random password
randomize_password: j = 0;
password_loop: p[j] = (char)(rand.Next(6) + 65);
j = j + 1;
if (j < 4) goto password_loop;
goto guess; // Start the game

// Game is complete - exit
end: _inout.WriteLine("Press any key to quit.");
_inout.Read();
}
}

A quick test run confirms that the application is working correctly:

Take a guess: AAAA
Take a guess: BBBB
Take a guess: CCCC
Take a guess: DDDD
+-++
Take a guess: DEDD
+++
Take a guess: DFDD
++++
Congratulations you guessed the password in 6 tries.

Press any key to quit.

Now, we can write a gold standard or characterization test that will verify all the parts of the code are working correctly. The only piece of the code this test will not cover is the random password generation:

public class GoldStandardTests
{
[Fact]
public void StandardTestRun()
{
// Arrange
var inout = new MockInputOutput();
var game = new Mastermind(inout);

// Arrange - Inputs
inout.InFeed.Enqueue("AAA");
inout.InFeed.Enqueue("AAAA");
inout.InFeed.Enqueue("ABBB");
inout.InFeed.Enqueue("ABCC");
inout.InFeed.Enqueue("ABCD");
inout.InFeed.Enqueue("ABCF");
inout.InFeed.Enqueue(" ");

// Arrange - Outputs
var expectedOutputs = new Queue<string>();
expectedOutputs.Enqueue("Take a guess: ");
expectedOutputs.Enqueue("Password length is 4." +
Environment.NewLine);
expectedOutputs.Enqueue("Take a guess: ");
expectedOutputs.Enqueue("+");
expectedOutputs.Enqueue("-");
expectedOutputs.Enqueue("-");
expectedOutputs.Enqueue("-");
expectedOutputs.Enqueue(Environment.NewLine);
expectedOutputs.Enqueue("Take a guess: ");
expectedOutputs.Enqueue("+");
expectedOutputs.Enqueue("+");
expectedOutputs.Enqueue("-");
expectedOutputs.Enqueue("-");
expectedOutputs.Enqueue(Environment.NewLine);
expectedOutputs.Enqueue("Take a guess: ");
expectedOutputs.Enqueue("+");
expectedOutputs.Enqueue("+");
expectedOutputs.Enqueue("+");
expectedOutputs.Enqueue("-");
expectedOutputs.Enqueue(Environment.NewLine);
expectedOutputs.Enqueue("Take a guess: ");
expectedOutputs.Enqueue("+");
expectedOutputs.Enqueue("+");
expectedOutputs.Enqueue("+");
expectedOutputs.Enqueue(Environment.NewLine);
expectedOutputs.Enqueue("Take a guess: ");
expectedOutputs.Enqueue("+");
expectedOutputs.Enqueue("+");
expectedOutputs.Enqueue("+");
expectedOutputs.Enqueue("+");
expectedOutputs.Enqueue(Environment.NewLine);
expectedOutputs.Enqueue("Congratulations you guessed the password
in 6 tries." + Environment.NewLine);
expectedOutputs.Enqueue("Press any key to quit." +
Environment.NewLine);
// Act
game.Play(new[] { "ABCF" });

// Assert
inout.OutFeed.ForEach((text) =>
{
Assert.Equal(expectedOutputs.Dequeue(), text);
});
}
}

This is an extremely long test, and it has an out of the ordinary structure, but this single test runs through almost all the logic in the application. You may not always be able to do this with a single test, but before beginning any heavy refactoring, these tests must exist.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.100.205