...
The HighestNumberFinder
is designed to return an integer representing the number in a group of numbers. This can easily be stubbed out.Here is the Stub
Begin by creating a new test
Code Block | ||
---|---|---|
| ||
namespace TopicManagerService {[Test] public class HighestNumberFinderpublic void find_heighest_score_with_array_of_one_return_array_of_one_using_stub() { { public int findHighestNumber(int[] values) // Arrange { return 89;int[] scores = { 56, 67, 45, 89 }; string topicName = "Physics"; }TopicScores[] topicScores = { new TopicScores(topicName, scores) } } |
But when you try and use this class in the tests, you should see type errors. This is because, in the implementation unit of TopicManager
, it's typed against HighestNumberFinder
in a namespace called FindHighestNumberService.v6
. So namespaces do not offer us the solution we are looking for.
Here are the modified tests
Code Block | ||
---|---|---|
| ||
using NUnit.Framework; namespace TopicManagerService {; publicIHighestNumberFinder hnf class= TopicManagerTests new TopicManagerServiceStubs.HighestNumberFinder(); { [Test]TopicManager cut = new TopicManager(hnf); public void find_heighest_score_in_empty_array_return_empty_array() TopicTopScore[] expectedResult = { new TopicTopScore(topicName, 89) }; // Arrange // Act TopicScores[] topicScores = Array.Empty<TopicScores>(); TopicTopScore[] HighestNumberFinder hnf = new HighestNumberFinder(result = cut.findTopicHighScores(topicScores); TopicManager cut = new TopicManager(hnf);// Assert TopicTopScore[] expectedResult = Array.Empty<TopicTopScore>(Assert.AreEqual(result[0].TopicName, expectedResult[0].TopicName); // ActAssert.AreEqual(result[0].TopScore, expectedResult[0].TopScore); } TopicTopScore[] result = cut.findTopicHighScores(topicScores);} |
Here is the Stub. We have put it in a different namespace in the test project
Code Block | ||
---|---|---|
| ||
namespace TopicManagerServiceStubs { public class HighestNumberFinder { // Assert public Assert.That(result, Is.EqualTo(expectedResult));int findHighestNumber(int[] values) } { [Test] public void find_heighest_score_with_array_of_one_return_array_of_one() 89; { } // Arrange int[] scores = { 56, 67, 45, 89 }; } } |
But when you try and use this class in the tests, you should see type errors in all the tests. This is because, in the implementation unit of TopicManager
, it's typed against HighestNumberFinder
in a namespace called FindHighestNumberService.v6
. So namespaces do not offer us the solution we are looking for.
Here are the updated tests. Alternatively, you could modify TopicManager
so it has a default constructor, but you will need to think about different issues and more tests for if TopicManager
default constructor is used.
Code Block | ||
---|---|---|
| ||
using NUnit.Framework; namespace TopicManagerService { public class TopicManagerTests { [Test] public void find_heighest_score_in_empty_array_return_empty_array() { string topicName = "Physics";// Arrange TopicScores[] topicScores = { new TopicScores(topicName, scores) }Array.Empty<TopicScores>(); HighestNumberFinder hnf = new HighestNumberFinder(); TopicManager cut = new TopicManager(hnf); TopicTopScore[] expectedResult = {new TopicTopScore(topicName, 89)}Array.Empty<TopicTopScore>(); // Act TopicTopScore[] result = cut.findTopicHighScores(topicScores); // Assert Assert.That(result, Is.EqualTo(expectedResult)); } [Test] // Assert public void find_heighest_score_with_array_of_one_return_array_of_one() Assert.AreEqual(result[0].TopicName, expectedResult[0].TopicName); { Assert.AreEqual(result[0].TopScore, expectedResult[0].TopScore); // Arrange } } } |
Observe errors on lines 14 and 32.
We need to use interfaces. And the interface needs to be in a place that is available to both production code and tests.
...
Create a new interface that is part of the HighestNumberFinder
project
...
language | c# |
---|
...
int[] scores = { 56, 67, 45, 89 }; |
...
string |
...
topicName = "Physics"; |
...
|
...
Copy HighestNumberFinder.v6 to HighestNumberFinderFinal
and refactor the class so that it implements the interface above
...
language | c# |
---|
...
|
...
|
...
|
...
|
...
|
...
|
...
TopicScores[] topicScores = { |
...
new TopicScores(topicName, scores) }; |
...
HighestNumberFinder hnf |
...
= |
...
new HighestNumberFinder(); |
...
TopicManager cut = new TopicManager(hnf); |
...
|
...
|
...
|
...
|
...
TopicTopScore[] |
...
expectedResult |
...
= {new |
...
TopicTopScore(topicName, 89)}; |
...
|
...
|
...
|
...
// Act |
...
TopicTopScore[] result = cut.findTopicHighScores(topicScores); |
...
// Assert |
...
|
...
|
...
Assert.AreEqual(result[0].TopicName, expectedResult[0].TopicName); |
...
Assert.AreEqual(result[0].TopScore, expectedResult[0].TopScore); } |
...
}
} |
To give us more flexibility, we need to use an interface. And the interface needs to be in a place that is available to both production code and tests.
Create a new interface that is part of the
HighestNumberFinder
production projectCode Block language c# namespace FindHighestNumberService { public interface IHighestNumberFinder { if (values[i] > result)int findHighestNumber(int[] values); result = values[i]; } }
Copy HighestNumberFinder.v6 to
HighestNumberFinderFinal
Refactor the class so that it implements the interface above
Code Block language c# using System; using FindHighestNumberService; namespace FindHighestNumberServiceFinal { public class EmptyArrayException : }Exception { return result;public EmptyArrayException(string message):base(message) } { } }
Refactor the tests so that the
HighestNumberFinder
is instantiated as followsCode Block } IHighestNumberFinder hnf = new FindHighestNumberServiceFinal.HighestNumberFinder();
Refactor
TopicManager
so it now works with the interfaceIHighestNumberFinder
and not directly with the implementation classRerun all your tests they should still be passing.
Tip |
---|
The tests have given us the confidence to refactor the code and begin to think more clearly about our implementation. |
Now let’s substitute a stub in place of the real implementation of HighestNumberFinder
.
Create a new called
find_heighest_score_with_array_of_one_return_array_of_one_using_stub()
, it's a copy offind_heighest_score_with_array_of_one_return_array_of_one()
Code Block language c# } public class HighestNumberFinder : IHighestNumberFinder { public int findHighestNumber(int[] values) { if (values.Length < 1) [Test] publicthrow void find_heighest_score_with_array_of_one_return_array_of_one_using_stub()new EmptyArrayException("Array is empty"); { int result = Int32.MinValue; //for Arrange(int i = 0; i < values.Length; i++) int[] scores = { 56, 67, 45, 89{ }; string topicName = "Physics"; if (values[i] > result) TopicScores[] topicScores = { new TopicScores(topicName, scores) }; result = values[i]; IHighestNumberFinder hnf = new FindHighestNumberServiceFinal.HighestNumberFinder(); } TopicManager cut = new TopicManager(hnf)return result; } TopicTopScore[] expectedResult = { new TopicTopScore(topicName, 89) }; } }
Modify the Stub version so it also implements
IHighestNumberFinder
Refactor
TopicManager
so it now works with the interfaceIHighestNumberFinder
and not directly with the implementation classCode Block language c# namespace TopicManagerServiceStubs { public class TopicManager { // Act private TopicTopScore[] result = cut.findTopicHighScores(topicScores)IHighestNumberFinder highestNumberFinder; // Assert Assert.AreEqual(result[0].TopicName, expectedResult[0].TopicName);public TopicManager( IHighestNumberFinder hnf ) { Assert.AreEqual(result[0].TopScore, expectedResult[0].TopScore); highestNumberFinder = hnf; } }
Modify line 8 to
Code Block language c# } IHighestNumberFinder hnf = new TopicManagerService.HighestNumberFinder();
The error is because of
TopicManagerTests.HighestNumberFinder
does not implement theIHighestNumberFinder
interface. Modify it so that it does.- Rerun the tests, they should all pass
...
Rerun all your tests they should still be passing.
Tip |
---|
The tests have given us the confidence to refactor the code and begin to think more clearly about our implementation. |
Info |
---|
We’ve now created a controlled environment for our tests |
QLC-2.3 Limitations of Stubs, complete this TopicManager requirement
Add more tests to handle the last two requirements
•If the input is [{“Physics”, {56, 67, 45, 89}}, {“Art”, {87, 66, 78}], the result should be [{“Physics”, 89}, {“Art”, 87}]
One Only one of the results will failtests passes. Why?
QLC-2.4 Mocks, complete the last TopicManager requirement
•If the input is [{“Physics”, {56, 67, 45, 89}}, {“Art”, {87, 66, 78}}, {“Comp Sci”, {45, 88, 97, 56}}], the result should be [{“Physics”, 89}, {“Art”, 87}, {“Comp Sci”, 97}]