TDD
FIND
The following exercise takes you through an example of what it's like to develop a feature for a system using TDD. For this example, imagine you have been asked to create a feature that counts occurrences of a character in a string.
public void ShouldFindOneYInMysterious()
{
var stringToCheck = "mysterious";
var stringToFind = "y";
var expectedResult = 1;
var classUnderTest = new StringUtilities();
var actualResult =
classUnderTest.CountOccurences(stringToCheck, stringToFind);
Assert.AreEqual(expectedResult, actualResult);
}
public int CountOccurences(string stringToCheck, string stringToFind)
{
var stringAsCharArray = stringToCheck.ToCharArray();
var stringToCheckForAsChar = stringToFind.Length-1;
var occuranceCount = 0;
for (var characterIndex = 0;
characterIndex < stringToCheckForAsChar;
characterIndex++)
{
if (stringAsCharArray[characterIndex] == stringToCheckForAsChar)
{
occuranceCount++;
}
}
return occuranceCount;
}
public int CountOccurences(string stringToCheck, string stringToFind)
{
var stringAsCharArray = stringToCheck.ToCharArray();
var stringToCheckForAsChar = stringToFind.ToCharArray()[0];
var occuranceCount = 0;
for (var characterIndex = 0;
characterIndex < stringAsCharArray.GetUpperBound(0);
characterIndex++)
{
if (stringAsCharArray[characterIndex] == stringToCheckForAsChar)
{
occuranceCount++;
}
}
return occuranceCount;
}
2 caracters
Upper
public void SearchShouldBeCaseSenstive()
{
var stringToCheck = "mySterious";
var stringToFind = "s";
var expectedResult = 2;
var classUnderTest = new StringUtilities();
var actualResult =
classUnderTest.CountOccurences(stringToCheck,
stringToFind);
Assert.AreEqual(expectedResult, actualResult);
}
public void ShouldFindTwoSInMysterious()
{
var stringToCheck = "mysterious";
var stringToFind = "s";
var expectedResult = 2;
var classUnderTest = new StringUtilities();
var actualResult = classUnderTest.CountOccurences(stringToCheck, stringToFind);
Assert.AreEqual(expectedResult, actualResult);
}
Upper Implementation
public int CountOccurences(string stringToCheck, string stringToFind)
{
var stringAsCharArray = stringToCheck.ToUpper().ToCharArray();
var stringToCheckForAsChar = stringToFind.ToUpper().ToCharArray()[0];
var occuranceCount = 0;
for (var characterIndex = 0;
characterIndex <= stringAsCharArray.GetUpperBound(0);
characterIndex++)
{
if (stringAsCharArray[characterIndex] == stringToCheckForAsChar)
{
occuranceCount++;
}
}
return occuranceCount;
}
Return -1
You deploy version one of your string utility class, and before long you have your first defect. When a user passes in a null as the string to be searched, a null reference exception is thrown. You can question the responsibility of the calling code to check its values before making the call, or argue that a null reference exception is appropriate; the string is null, after all. But the truth is that good developers realize that all input is evil and must be validated independently. And in the end, the business user would rather have the value −1 returned. You write a test to demonstrate this defect:
public int CountOccurences(string stringToCheck, string stringToFind)
{
if (stringToCheck == null) return −1;
var stringAsCharArray = stringToCheck.ToUpper().ToCharArray();
var stringAsCharArray = stringToCheck.ToUpper().ToCharArray();
var stringToCheckForAsChar = stringToFind.ToUpper().ToCharArray()[0];
var occuranceCount = 0;
for (var characterIndex = 0;
characterIndex <= stringAsCharArray.GetUpperBound(0);
characterIndex++)
{
if (stringAsCharArray[characterIndex] == stringToCheckForAsChar)
{
occuranceCount++;
}
}
return occuranceCount;
Complete
Setup
[TestClass]
public class ExampleTests
{
private string _testMessage;
[TestInitialize]
public void SetupForTest()
{
_testMessage = "This is a test.";
}
[TestMethod]
public void TestMethod()
{
Debug.WriteLine(_testMessage);
}
}
[TestClass]
public class ExampleTests
{
private string _testMessage;
[TestInitialize]
public void SetupForTest()
{
_testMessage = "This is a test.";
}
[TestMethod]
public void TestMethod()
{
Debug.WriteLine(_testMessage);
}
[TestCleanup]
public void TearDownAfterTest()
{
_testMessage = string.Empty;
}
}
TearDown
reset an environment variable or roll back a database transaction
Assert.AreEqual (expected, actual)
Assert.AreSame (expected, actual)
Assert.IsTrue (bool)/Assert.IsFalse(bool)
Assert.IsNull (object)/Assert.IsNotNull(object)
Assert.Greater (x,y)/Assert.GreaterOrEqual(x,y)
database, web service, file system, or other resource
DummyDependency
Mock
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnitTests
{
[TestClass]
public class DummyTestClass
{
[TestInitialize]
public void TestWithADummy()
{
var dependency = new DummyDependency();
var dependentClass = new DependentClass(dependency);
const string param = "abc";
const int expectedResultOne = 1;
var resultOne = dependentClass.GetValue(param);
Assert.AreEqual(expectedResultOne, resultOne);
}
}
public class DummyDependency : IDependency
{
public int GetValue(string s)
{
return 1;
}
}
}
database, web service, file system, or other resource
Result TDD with Mock
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnitTests
{
[TestClass]
public class DummyTestClass
{
[TestInitialize]
public void TestWithADummy()
{
var dependency = new DummyDependency();
var dependentClass = new DependentClass(dependency);
const string param = "abc";
const int expectedResultOne = 1;
var resultOne = dependentClass.GetValue(param);
Assert.AreEqual(expectedResultOne, resultOne);
}
}
public class DummyDependency : IDependency
{
public int GetValue(string s)
{
return 1;
}
}
}
STUB
[TestMethod]
public void TestWithAStub()
{
var dependency = new StubDependency();
var dependentClass = new DependentClass(dependency);
const string param1 = "abc";
const string param2 = "xyz";
const int expectedResultOne = 1;
const int expectedResultTwo = 2;
var resultOne = dependentClass.GetValue(param1);
var resultTwo = dependentClass.GetValue(param2);
Assert.AreEqual(expectedResultOne, resultOne);
Assert.AreEqual(expectedResultTwo, resultTwo);
}
public class StubDependency : IDependency
{
public int GetValue(string s)
{
if (s == "abc")
return 1;
if (s == "xyz")
return 2;
return 0;
}
}
using System;
internal interface IDependency
{
int GetValue(string s);
void CallMeFirst();
int CallMeTwice(string s);
void CallMeLast();
}
internal class DependentClass
{
private readonly IDependency _dependency;
public DependentClass(IDependency dependency)
{
_dependency = dependency;
}
public int GetValue(string s)
{
return _dependency.GetValue(s);
}
public void CallMeFirst()
{
_dependency.CallMeFirst();
}
public void CallMeLast()
{
_dependency.CallMeLast();
}
public int CallMeTwice(string s)
{
return _dependency.CallMeTwice(s);
}
}
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests
{
[TestClass]
public class MockTestClass
{
[TestMethod]
public void TestWithAMock()
{
// var dependency = new MockDependency();
var MockdependentClass = new DependentClass(new MockDependency());
const string param1 = "abc";
const string param2 = "xyz";
const int expectedResultOne = 1;
const int expectedResultTwo = 2;
MockdependentClass.CallMeFirst();
var resultOne = MockdependentClass.CallMeTwice(param1);
var resultTwo = MockdependentClass.CallMeTwice(param2);
MockdependentClass.CallMeLast();
Assert.AreEqual(expectedResultOne, resultOne);
Assert.AreEqual(expectedResultTwo, resultTwo);
}
public class MockDependency : IDependency
{
private int _callMeTwiceCalled;
private bool _callMeLastCalled;
private bool _callMeFirstCalled;
public int GetValue(string s)
{
if (s == "abc")
return 1;
if (s == "xyz")
return 2;
return 0;
}
public void CallMeFirst()
{
if ((_callMeTwiceCalled > 0) || _callMeLastCalled)
throw new AssertFailedException("CallMeFirst not first method called");
_callMeFirstCalled = true;
}
public int CallMeTwice(string s)
{
if (!_callMeFirstCalled)
throw new AssertFailedException("CallMeTwice called before CallMeFirst");
if (_callMeLastCalled)
throw new AssertFailedException("CallMeTwice called after CallMeLast");
if (_callMeTwiceCalled >= 2)
throw new AssertFailedException("CallMeTwice called more than twice");
_callMeTwiceCalled++;
return GetValue(s);
}
public void CallMeLast()
{
if (!_callMeFirstCalled)
throw new AssertFailedException("CallMeLast called before CallMeFirst");
if (_callMeTwiceCalled != 2)
throw new AssertFailedException(
string.Format("CallMeTwice not called { 0 } times",
_callMeTwiceCalled));
_callMeLastCalled = true;
}
}
}
}
TDD
By edisonvasquez
TDD
- 203