I am trying to write a unit test in Visual C# Unit Test
project.
I pass an empty Array
of Frame class and that returns a JSON
object.
[HttpPost]
public JsonResult SubmitBowlingScore(Frame[] frames)
{
int totalScore= 0;
var objScore = new EngineService();
for (int i = 0; i < frames.Length; i++)
{
Boolean wasSpare = false;
if (i > 0 && objScore.IsSpare(frames[i-1]))
{
wasSpare = true;
}
totalScore += objScore.CalculateScore(frames[i], wasSpare);
}
return Json("{\"score\":"+ totalScore + "}");
}
Hoping to test with the following entry: But no idea how!!!
[{""1stRoll"":2,""2ndRoll"":2 ,""3rdRoll"":0},
{""1stRoll"":4,""2ndRoll"":8 ,""3rdRoll"":0},
{""1stRoll"":6,""2ndRoll"":2 ,""3rdRoll"":0}];
Any help/idea/suggestion would be appreciated for the following unit test. How SubmitBowlingScore() would take the Frame [] data as a parameter?
[TestMethod]
public void SubmitBowlingScore()
{
//Arrange
HomeController controller = new HomeController();
//Act
JsonResult result = controller.SubmitBowlingScore(**What goes here???**) as JsonResult;
//Assert
Assert.IsNotNull(JsonResult, "No JsonResult returned from action method.");
Assert.AreEqual(@"{[{""1stRoll"":""2"",""2ndRoll"":2 ,""3rdRoll"":0},{""1stRoll"":""2"",""2ndRoll"":8 ,""3rdRoll"":0},{""1stRoll"":""6"",""2ndRoll"":2 ,""3rdRoll"":0}],""Count"":3,""Success"":true}",
result.Data.ToString());
}
You are mixing your business logic with your presentation logic. You should move the entire body of your controller method to an object that calculates the score based on the frames. Once you've got that, there's nothing to test in the controller (unless you don't trust the MVC framework..)
My rendition of your Frame model:
public class Frame
{
public int FirstRoll { get; set; }
public int SecondRoll { get; set; }
public int ThirdRoll { get; set; }
}
Here is the business logic as an extension. You might want to break this out into its own class or possibly make it a member of your EngineService class.
public static class FrameExtensions
{
public static int SumFrameScores(this Frame[] frames)
{
//break out early if no frames have been recorded
if (frames.Length == 0) return 0;
int totalScore = 0;
var objScore = new EngineService();
for (int i = 0; i < frames.Length; i++)
{
bool wasSpare = objScore.IsSpare(frames[i - 1]);
totalScore += objScore.CalculateScore(frames[i], wasSpare);
}
return totalScore;
}
}
When you're testing, you can test directly against your C# classes / types so you don't need to be worried about JSON/presentation layer data translation.
[TestMethod]
public void SubmitBowlingScore()
{
//Arrange
var frames = new Frame[]
{
new Frame {FirstRoll = 2, SecondRoll = 2, ThirdRoll = 0},
new Frame {FirstRoll = 2, SecondRoll = 6, ThirdRoll = 0},
new Frame {FirstRoll = 0, SecondRoll = 9, ThirdRoll = 0}
};
//Act
var score = frames.SumFrameScores();
//Assert
Assert.AreEqual(21, score);
}
Finally, your controller is reduced to the following:
[HttpPost]
public JsonResult SubmitBowlingScore(Frame[] frames)
{
var finalScore = frames.SumFrameScores();
return Json(new { score = finalScore });
}