In computer programming, unit testing is a method by which individual units of source code are tested to determine if they are fit for use. A unit is the smallest testable part of an application. In procedural programming a unit may be an individual function or procedure. Unit tests are created by programmers or occasionally by white box testers.
QUESTION:
Create a console application that takes in the coordinates of two points in a 2-dimensional plane and outputs the linear equation joining the two points similar to the one shown in Part I. In particular, the equation should be in the following form:
y=mx+b
Where m is the slope of the line and b is the y-intercept. Also, create unit test cases using the Microsoft Unit Testing Framework to thoroughly test your console application.
The format of the input parameter and output value are entirely up to you. Be sure to properly document all your code and note all of your assumptions in your main application as well as your unit test suite.
ANSWER:
To create the console application seems very easy, but there are some potential bugs you may want to consider when you write the code:
1, the input points’ coordinates contain no character or non-numeric character(s): wrong input, ask the user to re-enter the coordinate.
2, the x and y coordinates for pointA and pointB are the same: wrong input, ask the user to re-enter the x and y coordinates for the 2 points.
3, horizontal line: y=constant
4, vertical line: x=constant
5, m=1: the equation should be “y=x+b” instead of “y=1x+b”
6, m=-1: the equation should be “y=-x+b” instead of “y=-1x+b”
7, b=0: the equation should be “y=mx” instead of “y=mx+0″
8, b<0: e.g., b=-3, the equation should be "y=mx-3" instead of "y=mx+-3"
9, m and/or b are/is (a) decimal number(s): the decimal places should be limited as per spec (I assume the requirement is to limit the decimal places to 2 places).
First I wrote the code in VBScript, you can click here to download the script. Paste the VBScript in a notepad, and save it as “anyname.vbs”, then double click on the .vbs file to run it.
In the above VBScript, everything is handled in pop-up boxes (e.g., Input box and Message box), which is not a truly console application (black screen, DOS like looking). However, If you have Microsoft Visual Studio installed on your computer, then you can create a real console application by VB.Net. VBScript, VBA and VB.Net are very similar, if you know how to write VBScript or VBA, and use Google search, then you should be able to transfer your script to VB.Net.
A copy of the VB.Net code for the console application is pasted below. You will notice that the code below is very different than the VBScript mentioned above. There is only one function in the VBScript, which is called “Function OutputLinearEquation”. This function has been broken into 3 different functions in the VB.Net code. Why? All because the unit test.
All the functions that has logic should be tested by unit test, but in order to write the unit test, each function should only handle one small/simple thing, otherwise the function wont’ be unit test testable. In the unit test, there is expected values, and actual values, and you suppose to compare the expected and actual values to see if they are the same or not. If the function you wrote is complicated, and handles multiple things, such as the “OutputLinearEquation” function, then it will be impossible to identify which is the expected/actual value.
Transfer the VBScript to VB.Net wasn’t difficult, just some easy syntax changes, but when I start to write the unit test, I went back to the VB.Net code 3-5 times, to break the “OutputLinearEquation” function into multiple functions, each only handle a single thing. It took me 3 times more to write the unit test than to write the actual code. After the unit test was done, I fully understand why the developers in my company really don’t like to write unit tests – it takes too much time to write the unit test…
Think back, I should really think about the unit test first before I start to write the code. In that way, I may spend much less time. Actually, someone already figure this out long time before me, and they call this approach as “test-driven development”.
Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: first the developer writes a failing automated test case that defines a desired improvement or new function, then produces code to pass that test and finally refactors the new code to acceptable standards. Kent Beck, who is credited with having developed or ‘rediscovered’ the technique, stated in 2003 that TDD encourages simple designs and inspires confidence. For more information about Test-driven development, please read post#30.
A copy of the VB.Net code for the console application is pasted below.
Module Module1
Public x1 As Decimal, y1 As Decimal, x2 As Decimal, y2 As Decimal
Sub Main()
Call Input2points()
Console.WriteLine("The linear equation joins " & "pointA(" & x1 & "," & y1 & ") and pointB(" & x2 & "," & y2 & ") is" & vbCrLf & GetLinearEquation(x1, y1, x2, y2))
Console.WriteLine("Please click [Enter] key to exit.")
Console.ReadLine()
End Sub
'input 2 points' x and y coordinates
Public Sub Input2points()
Call Inputx1()
Call Inputy1()
Call Inputx2()
Call Inputy2()
'wrong input: the 2 points should not be the same
If x1 = x2 And y1 = y2 Then
Console.WriteLine("PointA(" & x1 & "," & y1 & ") and pointB(" & x2 & "," & y2 & ") are the same." & vbCrLf & "Please re-enter the X and Y coordinates for pointA and pointB.")
Input2points()
End If
End Sub
'handle wrong input, i.e., non-numeric character(s) or no character was entered
Public Sub Inputx1()
Dim orix1 As String
Try
Console.WriteLine("Please enter the X coordinate for pointA:")
orix1 = Console.ReadLine
x1 = Convert.ToDecimal(orix1)
Catch ex As Exception
Console.WriteLine("Non-numeric character or No character was entered.")
Inputx1()
End Try
End Sub
Public Sub Inputy1()
Dim oriy1 As String
Try
Console.WriteLine("Please enter the Y coordinate for pointA:")
oriy1 = Console.ReadLine
y1 = Convert.ToDecimal(oriy1)
Catch ex As Exception
Console.WriteLine("Non-numeric character or No character was entered.")
Inputy1()
End Try
End Sub
Public Sub Inputx2()
Dim orix2 As String
Try
Console.WriteLine("Please enter the X coordinate for pointB:")
orix2 = Console.ReadLine
x2 = Convert.ToDecimal(orix2)
Catch ex As Exception
Console.WriteLine("Non-numeric character or No character was entered.")
Inputx2()
End Try
End Sub
Public Sub Inputy2()
Dim oriy2 As String
Try
Console.WriteLine("Please enter the Y coordinate for pointB:")
oriy2 = Console.ReadLine
y2 = Convert.ToDecimal(oriy2)
Catch ex As Exception
Console.WriteLine("Non-numeric character or No character was entered.")
Inputy2()
End Try
End Sub
'get the linear equation which joins the 2 points
Public Function GetLinearEquation(ByVal x1, ByVal y1, ByVal x2, ByVal y2) As String
If x1 <> x2 Then
Dim params As Array
Try
params = CalculateLineParams(x1, y1, x2, y2)
Catch ex As Exception
GetLinearEquation = "Error! Cannot calculate m or b." & ex.ToString()
End Try
'use 2 decimals places in the output linear equationn
Dim m As Decimal, b As Decimal
m = FormatNumber(params(0), 2)
b = FormatNumber(params(1), 2)
GetLinearEquation = FormatLinearEquation(m, b)
Else 'special case: vertical line
GetLinearEquation = "x=" & x1
End If
End Function
'caculate the m and b value for the linear equation y=mx+b
Public Function CalculateLineParams(ByVal x1, ByVal y1, ByVal x2, ByVal y2) As Array
If x1 = x2 Then
Throw New ArgumentException("x1 = x2. It is a vertical line.")
Else
Dim params(2) As Decimal
params(0) = (y2 - y1) / (x2 - x1) 'original m
params(1) = y1 - params(0) * x1 'original b
Return params
End If
End Function
'format the linear equation y=mx+b which joins the 2 points
Public Function FormatLinearEquation(ByVal m, ByVal b) As String
If m <> 0 Then
'normal case: y=mx+b
If m <> 1 And m <> -1 And b > 0 Then
FormatLinearEquation = "y=" & m & "x+" & b
'special case: y=mx+0 should be displayed as y=mx
ElseIf m <> 1 And m <> -1 And b = 0 Then
FormatLinearEquation = "y=" & m & "x"
'special case: y=mx+-b should be displayed as y=mx-b
ElseIf m <> 1 And m <> -1 And b < 0 Then
FormatLinearEquation = "y=" & m & "x" & b
'special case: y=1x+b should be displayed as y=x+b
ElseIf m = 1 And b > 0 Then
FormatLinearEquation = "y=x+" & b
'special case: y=1x+0 should be displayed as y=x
ElseIf m = 1 And b = 0 Then
FormatLinearEquation = "y=x"
'special case: y=1x+-b should be displayed as y=x-b
ElseIf m = 1 And b < 0 Then
FormatLinearEquation = "y=x" & b
'special case: y=-1x+b should be displayed as y=-x+b
ElseIf m = -1 And b > 0 Then
FormatLinearEquation = "y=-x+" & b
'special case: y=-1x+0 should be displayed as y=-x
ElseIf m = -1 And b = 0 Then
FormatLinearEquation = "y=-x"
'special case: y=-1x+-b should be displayed as y=-x-b
ElseIf m = -1 And b < 0 Then
FormatLinearEquation = "y=-x" & b
End If
Else 'special case: horizontal line
FormatLinearEquation = "y=" & b
End If
End Function
End Module
I also created 5 unit test cases to test the 3 functions in the console application. The unit test cases were created in Microsoft Visual Studio by VB.Net too, and the Microsoft Unit Testing Framework is used:
A copy of the code for the 5 unit test cases is pasted below:
Imports System
Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports ConsoleApplication1
' a lot of code automatically generated by Microsoft Visual Studio
' these code won't show in this post
'A test for Function CalculateLineParams when x1 <> x2
_
Public Sub CalculateLineParamsNormalCaseTest()
Dim R = New Random
Dim x1 As Decimal = R.Next + R.NextDouble
Dim y1 As Decimal = R.Next + R.NextDouble
Dim x2 As Decimal = R.Next + R.NextDouble
Do While x2 = x1
x2 = R.Next + R.NextDouble
Loop
Dim y2 As Decimal = R.Next + R.NextDouble
Dim expected(1) As Decimal
expected(0) = y1
expected(1) = y2
Dim actual(1) As Decimal, params(1) As Decimal
params = Module1.CalculateLineParams(x1, y1, x2, y2)
actual(0) = params(0) * x1 + params(1)
actual(1) = params(0) * x2 + params(1)
Assert.AreEqual(actual(0), expected(0))
Assert.AreEqual(actual(1), expected(1))
End Sub
'A test for Function CalculateLineParams when x1 = x2
_
_
Public Sub CalculateLineParamsVerticalLineTest()
Dim R = New Random
Dim x1 As Decimal = R.Next + R.NextDouble
Dim y1 As Decimal = R.Next + R.NextDouble
Dim x2 As Decimal = x1
Dim y2 As Decimal = R.Next + R.NextDouble
Dim actual As Array = CalculateLineParams(x1, y1, x2, y2)
End Sub
'A test for Function FormatLinearEquation
_
Public Sub FormatLinearEquationTest()
Dim R = New Random
Dim m As Decimal, b As Decimal
Dim expected As String, actual As String
'special case: horizontal line
m = 0
b = R.Next + R.NextDouble
expected = "y=" & b
actual = Module1.FormatLinearEquation(m, b)
Assert.AreEqual(expected, actual)
'normal case: y=mx+b
m = R.Next + R.NextDouble
Do While m = 0 Or m = 1 Or m = -1
m = R.Next + R.NextDouble
Loop
b = R.Next(0) + R.NextDouble()
expected = "y=" & m & "x+" & b
actual = Module1.FormatLinearEquation(m, b)
Assert.AreEqual(expected, actual)
'special case: y=mx+0 should be displayed as y=mx
b = 0
expected = "y=" & m & "x"
actual = Module1.FormatLinearEquation(m, b)
Assert.AreEqual(expected, actual)
'special case: y=mx+-b should be displayed as y=mx-b
b = -R.Next(0) - R.NextDouble()
expected = "y=" & m & "x" & b
actual = Module1.FormatLinearEquation(m, b)
Assert.AreEqual(expected, actual)
'special case: y=1x+b should be displayed as y=x+b
m = 1
b = R.Next(0) + R.NextDouble()
expected = "y=x+" & b
actual = Module1.FormatLinearEquation(m, b)
Assert.AreEqual(expected, actual)
'special case: y=1x+0 should be displayed as y=x
b = 0
expected = "y=x"
actual = Module1.FormatLinearEquation(m, b)
Assert.AreEqual(expected, actual)
'special case: y=1x+-b should be displayed as y=x-b
b = -R.Next(0) - R.NextDouble()
expected = "y=x" & b
actual = Module1.FormatLinearEquation(m, b)
Assert.AreEqual(expected, actual)
'special case: y=-1x+b should be displayed as y=-x+b
m = -1
b = R.Next(0) + R.NextDouble()
expected = "y=-x+" & b
actual = Module1.FormatLinearEquation(m, b)
Assert.AreEqual(expected, actual)
'special case: y=-1x+0 should be displayed as y=-x
b = 0
expected = "y=-x"
actual = Module1.FormatLinearEquation(m, b)
Assert.AreEqual(expected, actual)
'special case: y=-1x+-b should be displayed as y=-x-b
b = -R.Next(0) - R.NextDouble()
expected = "y=-x" & b
actual = Module1.FormatLinearEquation(m, b)
Assert.AreEqual(expected, actual)
End Sub
'A test for Function GetLinearEquation when x1 <> x2
_
Public Sub GetLinearEquationNormalCaseTest()
Dim R = New Random
Dim x1 As Decimal = R.Next + R.NextDouble
Dim y1 As Decimal = R.Next + R.NextDouble
Dim x2 As Decimal = R.Next + R.NextDouble
Do While x2 = x1
x2 = R.Next + R.NextDouble
Loop
Dim y2 As Decimal = R.Next + R.NextDouble
Dim expected As String, actual As String
expected = "x=" & x1
actual = Module1.GetLinearEquation(x1, y1, x2, y2)
Assert.AreNotEqual(expected, actual)
End Sub
'A test for Function GetLinearEquation when x1 = x2
_
Public Sub GetLinearEquationVerticalLineTest()
Dim R = New Random
Dim x1 As Decimal = R.Next + R.NextDouble
Dim y1 As Decimal = R.Next + R.NextDouble
Dim x2 As Decimal = x1
Dim y2 As Decimal = R.Next + R.NextDouble
Dim expected As String, actual As String
expected = "x=" & x1
actual = Module1.GetLinearEquation(x1, y1, x2, y2)
Assert.AreEqual(expected, actual)
End Sub
End Class