Unit Testing Using Two Functions

For some time I neglected unit testing. I wrote unit tests, but they were pretty poor. I am currently reading The Art of Unit Testing to bring it to zero.

If I have an interface, for example:

public interface INotificationService { void AddError(string _error); void AddIssue(string _issue); IEnumerable<string> FetchErrors(); IEnumerable<string> FetchIssues(); } 

A specific implementation of this interface contains:

  private readonly ICollection<Message> messages; 

Adding an error or problem creates a new message with an enumeration indicating its type and adds it to the collection. Calling FetchErrors () / FetchIssues () returns messages of this type from the collection.

Will the following test be performed:

  [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 first call AddError () and then test the result of FetchErrors (). Therefore, I call two functions. This is not true?

Should I make the collection publicly available and explicitly state that it contains a message of the appropriate type containing a registered error message?

What would be the best practice in this scenario?

+7
c # unit-testing nunit
source share
4 answers

Your approach looks fine - testing implementation using public methods is the only access available in design.

To test methods independently, to check access to private messages, you will need to use a backdoor with a white box as a reflection to provide the appropriate Add* methods. This is not a good idea, as your tests will be interrupted (at runtime) every time the underlying CUT implementation changes.

A common alternative to reflection is to expose the non-interface internal method in the particular class being tested, which allowed you to gain some access to the "Maintenance hatch" test in the implementation field of ICollection<Message> messages; and then allow InternalsVisibleTo access to the Unit test assembly.

One note - you will find that Assert.AreEqual in an enumerated string will perform a reference comparison and therefore fail (because you are comparing it with a new array).

You will probably have to change this to something like:

 Assert.IsTrue(notificationService.FetchErrors().Contains("A new error")); 

Edit

IMO you donโ€™t have to go as far as making a public field in .Net. Since you already have an abstraction of the interface, which should theoretically limit the means of access to the CUT, the internal and InternalsVisibleTo realms are often used to allow special UT access. Jon Skeet et al. mentioned it here

 #region Unit Testing side-door internal ICollection<Message> Messages { get { return _messages }; // No Setter } #endregion 
+3
source share

Your approach is fine. A unit test should follow the Arrange-Act-Assert structure - and your notificationService.AddError(..) call is just part of the Arrange test section.

Not that you cannot call more than one method in a test (in many cases you will have to), the fact is that you only have to state / test only one fact . This is what you do, so everything looks good.

+2
source share

As others have pointed out, it is good to use both methods. You are testing class behavior, and every time an error message is posted, you can read it.

If the implementation is more complicated, you may need to rethink the design, as your method does too much. Looking at what you are describing, why do you really need a getter method?

In general, I do not like the idea of โ€‹โ€‹exposing the internal method for testing purposes only. This sends the wrong message to the one who will support this code in the future (even you, after you have forgotten). It says: "Yes, go ahead, access this collection directly from any class in this assembly."

+1
source share

We cannot say if your test is valid or not based on the code you posted. We do not know what AddError () does, so it is not possible to find out if the unit test is installed correctly.

If AddError simply adds an error to the collection and does something else, then your test is good, and the call to FetchErrors () is valid, because it must have a dedicated unit test that checks it. In any case, if your AddError method does something else, you need to modify the test and check all the steps that this method does.

+1
source share

All Articles