I've neglected unit testing for some time. I wrote unit tests, but they were fairly poor. I'm now reading through "The art of unit Testing" to bring myself up to scratch.
If I have an interface such as:
public interface INotificationService
{
void AddError(string _error);
void AddIssue(string _issue);
IEnumerable<string> FetchErrors();
IEnumerable<string> FetchIssues();
}
A concrete implementation of this interface contains:
private readonly ICollection<Message> messages;
Adding an error or issue creates a new message with an enum denoting it's type and adds it to the collection. Calling FetchErrors() / FetchIssues() returns messages of that type from the collection.
Would the following test be valid?:
[Test]
public void FetchErrors_LoggingEnabledAddErrorFetchErrors_ReturnsError()
{
notificationService = new NotificationService();
notificationService.AddError("A new error");
Assert.AreEqual(new []{"A new error"}, notificationService.FetchErrors());
}
My concern is that I'm first calling AddError(), then testing the result of FetchErrors(). So I'm calling two functions. Is this incorrect?
Should I make the collection public and directly assert that it contains a message of the appropriate type containing the logged error message?
What would be the best practice in this scenario?
Your approach looks OK - testing the implementation through the public methods is the only access made available in the design.
In order to test the methods independently of one another, you would need to use a white-box testing backdoor like reflection to access the private messages to ensure that the respective Add*
methods work. This isn't a good idea at all, since your tests will break (at run time) every time the underlying CUT implementation changes.
A common, better alternative to reflection is to expose a non-interface internal
method on the concrete class under test which allowed you some kind of 'maintenance hatch' access to the ICollection<Message> messages;
implementation field, and then allow InternalsVisibleTo
access to the Unit test assembly.
One note - you'll find that Assert.AreEqual
on an the string enumerable will do a reference comparison and thus fail (because you are comparing it to a new array).
You'll probably have to change this to something like:
Assert.IsTrue(notificationService.FetchErrors().Contains("A new error"));
Edit
IMO you don't need to go as far as making the field public
in .Net. Since you have the interface abstraction in place already which should in theory restrict the means of access to the CUT, it is quite common to use internal
scope and InternalsVisibleTo
to allow the UT special access. Jon Skeet et al have mentioned this here
#region Unit Testing side-door
internal ICollection<Message> Messages
{
get { return _messages };
// No Setter
}
#endregion
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With