...
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 }; string topicName = "Physics"; TopicScores[] topicScores = { new TopicScores(topicName, scores) }; HighestNumberFinder hnf = new HighestNumberFinder(); } } |
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 { TopicManager cut =[Test] new TopicManager(hnf); public void find_heighest_score_in_empty_array_return_empty_array() TopicTopScore[] expectedResult = {new TopicTopScore(topicName, 89)}; { // ActArrange TopicTopScoreTopicScores[] resulttopicScores = cutArray.findTopicHighScoresEmpty<TopicScores>(topicScores); // AssertHighestNumberFinder hnf = new HighestNumberFinder(); TopicManager cut = new Assert.AreEqual(result[0].TopicName, expectedResult[0].TopicNameTopicManager(hnf); TopicTopScore[] expectedResult = Array.Empty<TopicTopScore>(); Assert.AreEqual(result[0].TopScore, expectedResult[0].TopScore); // Act } TopicTopScore[] result } } |
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# |
---|
...
= cut.findTopicHighScores(topicScores); // Assert Assert.That(result, Is.EqualTo(expectedResult)); |
...
} |
...
|
...
[Test] |
...
|
...
Copy HighestNumberFinder.v6 to HighestNumberFinderFinal
and refactor the class so that it implements the interface above
...
language | c# |
---|
...
|
...
|
...
|
...
public |
...
void find_heighest_score_with_array_of_one_return_array_of_one() |
...
|
...
|
...
|
...
{ |
...
|
...
// Arrange |
...
|
...
|
...
|
...
int[] scores = { 56, 67, 45, 89 }; |
...
|
...
|
...
|
...
|
...
string |
...
topicName = "Physics"; |
...
|
...
|
...
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 { return result; int }findHighestNumber(int[] values); } }
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# [Test]
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 { public EmptyArrayException(string message):base(message) { } } public class HighestNumberFinder : IHighestNumberFinder { public void find_heighest_score_with_array_of_one_return_array_of_one_using_stub(int findHighestNumber(int[] values) { // Arrangeif (values.Length < 1) throw new EmptyArrayException("Array is empty"); int[] scores = { 56, 67, 45, 89 }; int result = Int32.MinValue; string topicName = "Physics"; for (int i = 0; i < values.Length; i++) TopicScores[] topicScores = { new TopicScores(topicName, scores) }; { IHighestNumberFinder hnf = new FindHighestNumberServiceFinal.HighestNumberFinder(); if (values[i] > result) TopicManager cut = new TopicManager(hnf); result = TopicTopScorevalues[i]; expectedResult = { new TopicTopScore(topicName, 89) }; } // Act return result; TopicTopScore[] result = cut.findTopicHighScores(topicScores); } } }
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 // Assert { Assert.AreEqual(result[0].TopicName, expectedResult[0].TopicName)private IHighestNumberFinder highestNumberFinder; public Assert.AreEqual(result[0].TopScore, expectedResult[0].TopScore);TopicManager( IHighestNumberFinder hnf ) { } }
Modify line 8 to
Code Block language c# highestNumberFinder 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 |
...
•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}]