What is the value of using ModelFactories over interfaces when it comes to unit testing?

Viewed 22

Looking at Microsoft's code for example, they create a 'ModelFactory' class for constructing objects for use in tests: https://github.com/Azure/azure-sdk-for-net/blob/62f2223e46c33825628443d11b8267de4e72a1c6/sdk/servicebus/Azure.Messaging.ServiceBus/src/Primitives/ServiceBusModelFactory.cs

So if we need to mock a method on a service bus client which returns say, 'SubscriptionProperties', we need to obtain a new instance of this object with all the correct min/max values as the real code will run validation against those fields:

public static SubscriptionProperties SubscriptionProperties(
            string topicName,
            string subscriptionName,
            TimeSpan lockDuration = default,
            bool requiresSession = default,
            TimeSpan defaultMessageTimeToLive = default,
            TimeSpan autoDeleteOnIdle = default,
            bool deadLetteringOnMessageExpiration = default,
            int maxDeliveryCount = default,
            bool enableBatchedOperations = default,
            EntityStatus status = default,
            string forwardTo = default,
            string forwardDeadLetteredMessagesTo = default,
            string userMetadata = default) =>
            new SubscriptionProperties(topicName, subscriptionName)
            {
                LockDuration = lockDuration,
                RequiresSession = requiresSession,
                DefaultMessageTimeToLive = defaultMessageTimeToLive,
                AutoDeleteOnIdle = autoDeleteOnIdle,
                DeadLetteringOnMessageExpiration = deadLetteringOnMessageExpiration,
                MaxDeliveryCount = maxDeliveryCount,
                EnableBatchedOperations = enableBatchedOperations,
                Status = status,
                ForwardTo = forwardTo,
                ForwardDeadLetteredMessagesTo = forwardDeadLetteredMessagesTo,
                UserMetadata = userMetadata
            };

If SubscriptionProperties instead implemented an interface with these properties, it would be extremely easy to mock the ones we care about without any real implementation executing.

What are some of the values which come with this 'real implementation' ModelFactory approach when it comes to unit testing?

1 Answers

SubscriptionProperties would probably not make a good interface. It would very likely lead to violations of the Interface Segregation Principle. I haven't spent the time necessary to understand if that truly would be an issue with SubscriptionProperties, but when I see very "fat" interfaces then the ISP alarm bell goes off in my head and I start looking for other SOLID violations.

If SubscriptionProperties instead implemented an interface with these properties, it would be extremely easy to mock the ones we care about without any real implementation executing.

Keep in mind that it's even easier to instantiate an object. Especially if it's a "humble" object, as SubscriptionProperties appears to be.

Unless you meant "if SubscriptionProperties instead implemented several slim interfaces, each holding only a couple of the relevant properties".

So if we need to mock a method on a service bus client which returns say, 'SubscriptionProperties', we need to obtain a new instance of this object with all the correct min/max values as the real code will run validation against those fields.

If that ends up being the case, then I would bet dollars to donuts that the test is not a unit test but is instead approaching the integration end of the spectrum. Perhaps it would be wiser to break up such a "large" test into smaller tests each responsible for testing a smaller piece of the pie. And by the time that happens I would be surprised if SubscriptionProperties appears on the scene at all.

And this is purely speculation, but perhaps the authors of that library thought it would be easier to refactor method calls versus object instantiation?

This is all very subjective, so take it all with a few grains of salt :)

Related