3

I'm doing (or trying to do) TDD to develop my business logic for an application.

I have a collection in one class that I expose as IReadOnlyList, because I want to make it clear that adding/removing from the list is prohibited. Instead I expose AddItem and RemoveItem methods, so that I can execute some other code when an item is added or removed. See example below.

public interface IBusinessItem { }
public interface IBusinessClass
{
    IReadOnlyList<IBusinessItem> Items { get; }

    void AddItem(IBusinessItem item);
    void RemoveItem(IBusinessItem item);
}

My problem arises when I try to follow the principle that my unit tests should fail for one reason only. Unit testing my RemoveItem requires that I make use of AddItem in my test arrange phase. Therefore, my test can fail either because the RemoveItem does not remove an item as expected, or because the AddItem didn't work as expected so RemoveItem throws:

[Fact]
public void RemoveItemShouldRemoveItemFromItems()
{
    // Arrange
    var item = new MyBusinessItem();
    sut.AddItem(item);
    // Act
    sut.RemoveItem(item); // throws exception if sut.Items is empty
    // Assert
    Assert.Empty(sut.Items);
}

I'm thinking that my unit test would be less fragile if I could access sut.Items.Add(item), instead of relying on sut.AddItem(item), but I don't want to change my public interface just for testing purposes.

I think this answer partially adresses my concerns. My interpretation of the answer (and one of Mark's comments to the answer) is that it is fine to invoke other methods in the arrange phase. But I feel like this could end up with a lot of tests that fails for other reasons than what they are actually testing.

My question here is: When doing TDD, is it acceptable to access the underlying List.Add (that is not part of my public interface) in order avoid dependency of the AddItem implementation in my RemoveItem tests?.

A few ways I can think of to access the underlying List.Add:

  • Casting it in the test
  • Changing the underlying private field to protected and create a mock that inherits from my BusinessClass and exposes that field
  • Changing the underlying private field to internal and use the InternalsVisibleTo attribute
samirem
  • 125
  • 2
  • 9
  • 2
    If You want to expose something, just create a Mock, Stub, ... version of it inheriting from the basic class. The inherited version may expose what You want. No need for InternalsVisibleTo. – ntohl Sep 19 '17 at 09:16
  • I would suggest adding a constructor dedicated for tests that takes a collection of items. In the constructor initialize the underlying collection directly, without calling AddItem. That way your arrange phase won't depend on AddItem. – Krzysztof Jelski Sep 19 '17 at 16:02

0 Answers0