Reputation: 540
I'm currently attempting to implement the humble object design pattern. The only resource I can find regarding this is from the xunit testing web site. Unfortunately, I don't really understand it despite reading it several times. Is there perhaps another name this pattern goes by?
Here is what I have thus far.
public interface IHumbleExchangeWebService
{
GetItemResult BindToItems(IEnumerable<string> itemIds);
SyncFolderItemsResult Sync();
void Subscribe();
}
public class ExchangeWebServiceAdapter
{
private readonly IHumbleExchangeWebService _humbleExchangeWebService;
public ExchangeWebServiceAdapter(
IHumbleExchangeWebService humbleExchangeWebService)
{
_humbleExchangeWebService = humbleExchangeWebService;
Start();
}
private void Start()
{
SyncFolderItemsResult syncFolderItemsResults;
while(true)
{
syncFolderItemsResults = _humbleExchangeWebService.Sync();
if(syncFolderItemsResults != null)
break;
}
ProcessSyncResults(syncFolderItemsResults);
LastSyncDateTime = Time.Now;
_humbleExchangeWebService.Subscribe();
}
private void ProcessSyncResults(SyncFolderItemsResult syncFolderItemsResults)
{
if (syncFolderItemsResults.Count <= 0) return;
var missedItemIds = syncFolderItemsResults.ItemChange
.ToList()
.Select(x => x.ExchangeItem.ItemId);
_humbleExchangeWebService.BindToItems(missedItemIds);
}
public DateTime LastSyncDateTime { get; private set; }
}
And as for the testing:
[TestFixture]
public class Tests
{
private Mock<IHumbleExchangeWebService> _humbleExchangeWebServiceMock;
[SetUp]
public void SetUp()
{
_humbleExchangeWebServiceMock = new Mock<IHumbleExchangeWebService>();
}
[Test]
public void OnInitialiseWillSyncBeforeSubscribing()
{
var callOrder = 0;
_humbleExchangeWebServiceMock
.Setup(x => x.Sync())
.Returns(() => new SyncFolderItemsResult(0, false, String.Empty, GetExchangeItemChangeList()))
.Callback(() => Assert.That(callOrder++, Is.EqualTo(0)));
_humbleExchangeWebServiceMock
.Setup(x => x.Subscribe())
.Callback(() => Assert.That(callOrder++, Is.EqualTo(1)));
var service = GetConstructedService();
_humbleExchangeWebServiceMock.Verify(x => x.Sync(), Times.Once());
_humbleExchangeWebServiceMock.Verify(x => x.Subscribe(), Times.Once());
}
[Test]
public void WhenSyncingIsCompleteWillProcessMissingItem()
{
_humbleExchangeWebServiceMock
.Setup(x => x.Sync())
.Returns(new SyncFolderItemsResult(1, false, It.IsAny<string>(), GetExchangeItemChangeList()));
var service = GetConstructedService();
_humbleExchangeWebServiceMock.Verify(x => x.BindToItems(It.IsAny<IEnumerable<string>>()));
}
[Test]
public void BindingItemsWillProcess()
{
_humbleExchangeWebServiceMock
.Setup(x => x.Sync())
.Returns(new SyncFolderItemsResult(1, false, It.IsAny<string>(), GetExchangeItemChangeList()));
var service = GetConstructedService();
_humbleExchangeWebServiceMock
.Verify(x => x.BindToItems(new []{"AAA", "BBB", "CCC", "DDD"}), Times.Once());
_humbleExchangeWebServiceMock.VerifyAll();
}
private ExchangeWebServiceAdapter GetConstructedService()
{
return new ExchangeWebServiceAdapter(_humbleExchangeWebServiceMock.Object);
}
private IEnumerable<ExchangeItemChange> GetExchangeItemChangeList()
{
yield return new ExchangeItemChange(ChangeType.Create, new ExchangeItem("AAA"));
yield return new ExchangeItemChange(ChangeType.Create, new ExchangeItem("BBB"));
yield return new ExchangeItemChange(ChangeType.Create, new ExchangeItem("CCC"));
yield return new ExchangeItemChange(ChangeType.Create, new ExchangeItem("DDD"));
}
}
Essentially, I'm simply wondering if I'm on the right track of making the humble object dumb. What I mean is that is it the proper way to extract all conditions that I can easily unit test into a wrapper class (e.g. ExchangeWebServiceAdapter
).
Upvotes: 0
Views: 1706
Reputation: 7656
A good rule of thumb when trying to implement the humble object pattern is to look at the object you just created and ask yourself how you're going to test all the logic inside it. If you have a general idea of what you're going to do within 10 seconds and it doesn't cause you to groan out loud, you're probably fine. If it takes you any longer than that or if it causes you pain to think about it, you've probably made a mistake somewhere.
As far as understanding of the humble pattern, you can think of it like a car assembly line; the thing that makes those massive lines work is the fact that everything is interchangeable. Engines only Go, heaters only Heat, etc. The simplest way to keep it straight in your mind is to name your classes such that they are a concrete object, and then get rid of anything that doesn't fall under what that object should do. In your specific example, you've named your class an 'adapter'. Okay, so what is it adapting FROM and TO? What two incompatible things is it making compatible? I'd guess part of your confusion is that your class is misnamed or too broad at this point in time. As your understanding grows so will your vocabulary and you might find it's perfectly fine at that point. If you're just learning the pattern, it will inhibit your understanding to jump straight to programmer jargon; a lot of these words we use have poorly defined meanings.
To put it more concretely, if a Starter needs to get other information or kick other resources to finish Starting, that's fine; it's just that those other resources should be clearly named, abstracted out to other classes, and tested separately.
You've done a decent job of making the objects as you've presented them trivial, but obviously you'll need to worry about the implementation of your interface being dumb enough as well, when you get to that point.
Hopefully this helps; the humble object pattern is both distinct and very similar to the ideal of loose coupling that you will hear people talk about all the time. The humble object pattern is simply a specific prescribed way of achieving loose coupling with a bunch of objects that traditionally resist it.
Upvotes: 4