Today, I want to try a test. I want to see if I can wedge some Test Driven Development(TDD) into this series. Not sure if it will work but lets give it a try and dig into Noob to Pro Test Driven Development.
What to test in our Noob to Pro Test Driven Development?
As you may recall from a previous post, we still have quite a bit of business logic living in the GUI in terms of the operator button’s click events. We need to pull those out. Here is where we are currently.
FormCalc
using System; using System.Windows.Forms; using System.Collections.Generic; namespace NoobToPro { public partial class FormCalc : Form { List<Number> values; const string ONE = "1"; const string TWO = "2"; const string THREE = "3"; const string FOUR = "4"; const string FIVE = "5"; const string SIX = "6"; const string SEVEN = "7"; const string EIGHT = "8"; const string NINE = "9"; const string ZERO = "0"; const string PERIOD = "."; const string ADD = "+"; const string SUBTRACT = "-"; const string DIVIDE = "/"; const string MULTIPLE = "*"; const string EQUALS = "="; public FormCalc() { InitializeComponent(); } private void btn7_Click(object sender, EventArgs e) { txtInput.Text += SEVEN; } private void btn8_Click(object sender, EventArgs e) { txtInput.Text += EIGHT; } private void btn9_Click(object sender, EventArgs e) { txtInput.Text += NINE; } private void btn4_Click(object sender, EventArgs e) { txtInput.Text += FOUR; } private void btn5_Click(object sender, EventArgs e) { txtInput.Text += FIVE; } private void btn6_Click(object sender, EventArgs e) { txtInput.Text += SIX; } private void btn1_Click(object sender, EventArgs e) { txtInput.Text += ONE; } private void btn2_Click(object sender, EventArgs e) { txtInput.Text += TWO; } private void btn3_Click(object sender, EventArgs e) { txtInput.Text += THREE; } private void btn0_Click(object sender, EventArgs e) { txtInput.Text += ZERO; } private void btnPeriod_Click(object sender, EventArgs e) { txtInput.Text += PERIOD; } private void btnDivide_Click(object sender, EventArgs e) { Number temp = new Number(); temp.sign = DIVIDE; temp.ParseSetValue(txtInput.Text); values.Add(temp); txtInput.Text = string.Empty; } private void btnMultiply_Click(object sender, EventArgs e) { Number temp = new Number(); temp.sign = MULTIPLE; temp.ParseSetValue(txtInput.Text); values.Add(temp); txtInput.Text = string.Empty; } private void btnSubtract_Click(object sender, EventArgs e) { Number temp = new Number(); temp.sign = SUBTRACT; temp.ParseSetValue(txtInput.Text); values.Add(temp); txtInput.Text = string.Empty; } private void btnAdd_Click(object sender, EventArgs e) { Number temp = new Number(); temp.sign = ADD; temp.ParseSetValue(txtInput.Text); values.Add(temp); txtInput.Text = string.Empty; } private void btnEquals_Click(object sender, EventArgs e) { string lastSign; Number final = new Number(); Number temp = new Number(); temp.ParseSetValue(txtInput.Text); values.Add(temp); if(values.Count >= 1) { final = values[0]; values.RemoveAt(0); lastSign = final.sign; foreach (Number val in values) { switch (lastSign) { case ADD: final.value += val.value; break; case SUBTRACT: final.value -= val.value; break; case MULTIPLE: final.value *= val.value; break; case DIVIDE: final.value /= val.value; break; } lastSign = val.sign; } } lblResult.Text = final.value.ToString(); values = new List<Number>(); lastSign = string.Empty; txtInput.Text = string.Empty; } private void FormCalc_Load(object sender, EventArgs e) { lblResult.Text = string.Empty; values = new List<Number>(); } } }
Number
using System; namespace NoobToPro { public class Number { public float value { get; set; } public string sign { get; set; } public void ParseSetValue(string valueToParse) { bool parseSuccess; float temp; parseSuccess = float.TryParse(valueToParse, out temp); if (parseSuccess) { value = temp; } else { throw new Exception("Value was not a number."); } } } }
Because of how long this post could get if we went through each function, I have decided we will look at the equals function. The reality of the matter is that the add, subtract, multiple and divide are just setting values and not really doing any calculation so the test just confirms the set was done. Hardly a unit test worth exploring in depth.
So here is where we are with those functions all set.
FormCalcController.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace NoobToPro { public class FormCalcController { public List<Number> values { get; set; } const string ADD = "+"; const string SUBTRACT = "-"; const string DIVIDE = "/"; const string MULTIPLY = "*"; public FormCalcController() { values = new List<Number>(); } public void setAddition(string inputValue) { Number temp = new Number(); temp.sign = ADD; temp.ParseSetValue(inputValue); values.Add(temp); } public void setSubtract(string inputValue) { Number temp = new Number(); temp.sign = SUBTRACT; temp.ParseSetValue(inputValue); values.Add(temp); } public void setMultiply(string inputValue) { Number temp = new Number(); temp.sign = MULTIPLY; temp.ParseSetValue(inputValue); values.Add(temp); } public void setDivide(string inputValue) { Number temp = new Number(); temp.sign = DIVIDE; temp.ParseSetValue(inputValue); values.Add(temp); } } }
FormCalcController_Tests.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using NUnit.Framework; using NoobToPro; namespace NoobToPro_Tests { [TestFixture] public class FormCalcController_Tests { FormCalcController fcc; [SetUp] public void SetUp() { fcc = new FormCalcController(); } [TestCase("1.0", 1f)] public void setAddition_test(string inputValue, float expectedValue) { //arrange //act fcc.setAddition(inputValue); //assert Assert.IsTrue(fcc.values.First().sign == "+"); Assert.AreEqual(fcc.values.First().value, expectedValue); } [TestCase("1.0", 1f)] public void setSubtract_test(string inputValue, float expectedValue) { //arrange //act fcc.setSubtract(inputValue); //assert Assert.IsTrue(fcc.values.First().sign == "-"); Assert.AreEqual(fcc.values.First().value, expectedValue); } [TestCase("1.0", 1f)] public void setMultiply_test(string inputValue, float expectedValue) { //arrange //act fcc.setMultiply(inputValue); //assert Assert.IsTrue(fcc.values.First().sign == "*"); Assert.AreEqual(fcc.values.First().value, expectedValue); } [TestCase("1.0", 1f)] public void setDivision_test(string inputValue, float expectedValue) { //arrange //act fcc.setDivide(inputValue); //assert Assert.IsTrue(fcc.values.First().sign == "/"); Assert.AreEqual(fcc.values.First().value, expectedValue); } [TearDown] public void TearDown() { fcc = null; } } }
Lets start with adding the unit test that fails for equals. Since we want to control the operations. We will create one for addition.
[TestCase("3.5", "4", "5.0", 12.5f)] public void runEquation_sum_Test(string value1, string value2, string value3, float expectedResult) { //arrange float result; fcc.setAddition(value1); fcc.setAddition(value2); fcc.setAddition(value3); //act result = fcc.setEquals(); //assert Assert.AreEqual(expectedResult, result); }
This test failed because there is no setEquals function so lets create it in the FormCalcController.
public float setEquals() { return 0; }
Now we can run our test but since it is just returning 0, we need to add some logic. Now we could just have that function hard code to addition then it would pass. When we tried subtract it would fail. Since we know this is going to happen, lets code it to handle whatever in terms of signs.
We just happen to have all that code already from our previous work so lets add it and clean it up.
public float setEquals() { string lastSign; Number final = new Number(); //Number temp = new Number(); //temp.ParseSetValue(txtInput.Text); //values.Add(temp); if (values.Count >= 1) { final = values[0]; values.RemoveAt(0); lastSign = final.sign; foreach (Number val in values) { switch (lastSign) { case ADD: final.value += val.value; break; case SUBTRACT: final.value -= val.value; break; case MULTIPLY: final.value *= val.value; break; case DIVIDE: final.value /= val.value; break; } lastSign = val.sign; } } //lblResult.Text = final.value.ToString(); values = new List<Number>(); lastSign = string.Empty; return final.value; //txtInput.Text = string.Empty; }
To solve the errors, I commented out all the things that are GUI concerns. I left them in here so you can see what I did. I also added at the end a return of the final value. Remember these commented items when it comes time to integrate our new code.
So our addition test comes back good. Lets test the subtraction.
[TestCase("3.5", "4", "5.0", -5.5f)] public void runEquation_difference_Test(string value1, string value2, string value3, float expectedResult) { //arrange float result; fcc.setSubtract(value1); fcc.setSubtract(value2); fcc.setSubtract(value3); //act result = fcc.setEquals(); //assert Assert.AreEqual(expectedResult, result); }
That passed as well so I will skip ahead and make the multiplication and division values.
[TestCase("3.5", "4", "5.0", 70f)] public void runEquation_product_Test(string value1, string value2, string value3, float expectedResult) { //arrange float result; fcc.setMultiply(value1); fcc.setMultiply(value2); fcc.setMultiply(value3); //act result = fcc.setEquals(); //assert Assert.AreEqual(expectedResult, result); } [TestCase("3.5", "4", "5.0", 0.175f)] public void runEquation_quotient_Test(string value1, string value2, string value3, float expectedResult) { //arrange float result; fcc.setDivide(value1); fcc.setDivide(value2); fcc.setDivide(value3); //act result = fcc.setEquals(); //assert Assert.AreEqual(expectedResult, result); }
After these there really is only one test left. Multiple signs so lets make a multiple sign test.
[TestCase("3.5", "4", "5.0", 0.175f)] public void runEquation_multi_Add_Sub_Mult_Test(string value1, string value2, string value3, float expectedResult) { //arrange float result; fcc.setAddition(value1); fcc.setSubtract(value2); fcc.setMultiply(value3); //act result = fcc.setEquals(); //assert Assert.AreEqual(expectedResult, result); }
This one failed but why? Well if you remember some of the lines of code we commented out earlier in the setEquals function you will remember the part where it captures the last value. When we were running the all same sign they did what we thought. However, when it is dynamic it got hung. It was hanging the above tests too but we did not notice it. So lets fix that.
public float setEquals(string finalValue) { string lastSign; Number final = new Number(); Number temp = new Number(); temp.ParseSetValue(finalValue); values.Add(temp); if (values.Count >= 1) { final = values[0]; values.RemoveAt(0); lastSign = final.sign; foreach (Number val in values) { switch (lastSign) { case ADD: final.value += val.value; break; case SUBTRACT: final.value -= val.value; break; case MULTIPLY: final.value *= val.value; break; case DIVIDE: final.value /= val.value; break; } lastSign = val.sign; } } //lblResult.Text = final.value.ToString(); values = new List<Number>(); lastSign = string.Empty; return final.value; //txtInput.Text = string.Empty; }
We uncommented the temp value and switched it source from the textbox to a parameter. In doing this we broke our previously successful unit tests since they did not provide that parameter. It is an easy fix.
[TestCase("3.5", "4", "5.0", 12.5f)] public void runEquation_sum_Test(string value1, string value2, string value3, float expectedResult) { //arrange float result; fcc.setAddition(value1); fcc.setAddition(value2); //act result = fcc.setEquals(value3); //assert Assert.AreEqual(expectedResult, result); } [TestCase("3.5", "4", "5.0", -5.5f)] public void runEquation_difference_Test(string value1, string value2, string value3, float expectedResult) { //arrange float result; fcc.setSubtract(value1); fcc.setSubtract(value2); //act result = fcc.setEquals(value3); //assert Assert.AreEqual(expectedResult, result); } [TestCase("3.5", "4", "5.0", 70f)] public void runEquation_product_Test(string value1, string value2, string value3, float expectedResult) { //arrange float result; fcc.setMultiply(value1); fcc.setMultiply(value2); //act result = fcc.setEquals(value3); //assert Assert.AreEqual(expectedResult, result); } [TestCase("3.5", "4", "5.0", 0.175f)] public void runEquation_quotient_Test(string value1, string value2, string value3, float expectedResult) { //arrange float result; fcc.setDivide(value1); fcc.setDivide(value2); //act result = fcc.setEquals(value3); //assert
We removed the value 3 from the arrange section and brought it down as the parameter for our function.
In out multi-sign test we brought it out to the fourth value.
[TestCase("3.5", "4", "5.0", "1.9", 4.75f)] public void runEquation_multi_Add_Sub_Mult_Test(string value1, string value2, string value3, string value4, float expectedResult) { //arrange float result; fcc.setAddition(value1); fcc.setSubtract(value2); fcc.setMultiply(value3); //act result = fcc.setEquals(value4); //assert Assert.AreEqual(expectedResult, result); }
With that all wrapped up, we can focus on attaching it to the program. However this post is getting crazy long so lets wrap it here. If you want a walk through of the add, subtract multiple and divide sections then leave a comment down below. If I get enough interest. I will go back and walk through that with you.
Hopefully this is helpful introduction to Test Driven Development as you go from a noob to a pro.