Necoras
Necoras

Reputation: 7552

How do I unit test a public method which utilizes a private property?

I have a class which is basically a pipeline. It processes messages and then deletes them in batches. In order to do this the ProcessMessage() method doesn't directly delete messages; it adds them to a private Observable<IMessage>(). I then have another public method which watches that observable and deletes the messages en masse.

That results in code similar to:

public void CreateDeletionObservable(int interval = 30, int messageCount = 10)
{
    this.processedMessages.Buffer(TimeSpan.FromSeconds(interval), messageCount).Subscribe(observer =>
    {
        client.Value.DeleteMessages(observer.ToList());
    });
}

The problem is that my unit test doesn't have a value for processedMessages. I can't provide a moq'd value as it's private. I don't need to test what values are in processedMessages; I just need for them to exist in order to test that method's behavior. Specifically I need to test that my observable will continue running if an exception is thrown (that logic isn't in the code yet). As I see it I have a few options:

1) Refactor my class to use a single monster observable chain with a single entry point and a few exits (success, error, retry, etc.). This would avoid the use of private properties to pass collections around between public methods. However, that chain would be extremely difficult to parse much less unit test. I don't believe that making my code less readable and testable is a viable option.

2) Modify my CreateDeletionObservable method to accept a test list of Messages:

    public void CreateDeletionObservable(int interval = 30, int messageCount = 10, IObservable<IMessage> processedMessages = null)

That would allow me to supply stubbed data for the method to use, but it's a horrible code smell. A variation on this is to inject that Observable at the constructor level, but that's no better. Possibly worse.

3) Make processedMessages public.

4) Don't test this functionality.

I don't like any of these options, but I'm leaning towards 2; injecting a list for testing purposes. Is there an option I'm missing here?

Upvotes: 0

Views: 109

Answers (1)

Lee Campbell
Lee Campbell

Reputation: 10783

Your senses serve you well. I think in this case you can revert to guidance I find useful of "Test your boundaries" (Udi Dahan, but cant find the reference).

It seems that you can input message (via an Observable Sequence) and that as a side effect you will eventually delete these messages from the Client. So it seems that your test should read something like

  • "Given an EventProcessor, When 10 Messages are Processed, Then the Events are deleted from the client"
  • "Given an EventProcessor, When 5 Messages are Processed in 30s, Then the Events are deleted from the client"

So instead of testing this small part of the pipe that somehow knows about this.processedMessages (where did that instance come from?), test the chain. But this doesn't mean you need to create a massive unusable chain. Just create enough of the chain to make it testable.

Providing more of the code base would also help, e.g. where does this.processedMessages & client.Value come from? This is probably key and at a guess applying a more functional approach might help?

Upvotes: 1

Related Questions