Reputation: 2054
I know there are questions are are similar to this, but none seem to satisfy the correct way to address the following code snippet in regards to unit testing
public class InMemoryQueue : ICloudQueue
{
private Queue<object> _inMemoryCollection = new Queue<object>();
private readonly ServiceBusQueueSettings _settings;
public InMemoryQueue(ServiceBusQueueSettings settings = null)
{
_settings = settings;
if (_settings == null) _settings = new ServiceBusQueueSettings();
}
public async Task<IEnumerable<T>> ReceieveAsync<T>(int batchCount)
{
List<T> result = new List<T>();
for (int i = 0; i < batchCount; i++)
{
_inMemoryCollection.Dequeue();
}
return result;
}
public async Task AddToAsync<T>(IEnumerable<T> items)
{
items.ToList().ForEach(x=>_inMemoryCollection.Enqueue(x));
}
}
OK, I want to unit this this implementation of ICloudQueue to ensure that items that are added to via AddToAsync are held in memory, and any call to RecieveAsync is pulled from memory.
However - the complication is that the data is held in _inMemoryCollection which is a private variable. If the _inMemoryCollection queue WASNT private, one of the tests might look like this :
[Test]
public async Task Adding_To_Queue_Results_In_Message_Added_To_List()
{
//arrange
var listToAdd = new List<object>() { new object() };
var inMemoryQueue = new InMemoryQueue();
//act
await inMemoryQueue.AddToAsync(listToAdd);
//assert
Assert.IsTrue(inMemoryQueue._inMemoryCollection.Count == 1);
}
I guess I could change the _inMemoryCollection to be internal, then use the [InternalsVisibleTo] attribute to expose it to the unit tests. Although I'm reluctant as it means changing the implementation just to satisfy unit tests.
Is this my only option?
Upvotes: 2
Views: 393
Reputation: 16878
A real implementation of the queue within InMemoryQueue
can be provided by the dependency injection (by constructor for example), which will allow you to test it:
public class InMemoryQueue : ICloudQueue
{
private IQueue<object> _queue;
private readonly ServiceBusQueueSettings _settings;
public InMemoryQueue(IQueue<object> queue, ServiceBusQueueSettings settings = null)
{
_queue = queue;
_settings = settings;
if (_settings == null) _settings = new ServiceBusQueueSettings();
}
public async Task<IEnumerable<T>> ReceieveAsync<T>(int batchCount)
{
List<T> result = new List<T>();
for (int i = 0; i < batchCount; i++)
{
_queue.Dequeue();
}
return result;
}
public async Task AddToAsync<T>(IEnumerable<T> items)
{
items.ToList().ForEach(x => _queue.Enqueue(x));
}
}
Unfortunatelly, System.Collections.Generic.Queue<T>
do not implement any IQueue<T>
interface, so you must to introduce it:
public interface IQueue<T>
{
void Enqueue(T item);
T Dequeue();
int Count { get; }
}
So to test InMemoryQueue
with Queue<T>
, a wrapper must be written:
public class QueueWrapper<T> : IQueue<T>
{
private Queue<T> queue;
public QueueWrapper()
{
queue = new Queue<T>();
}
public void Enqueue(T item)
{
queue.Enqueue(item);
}
public T Dequeue()
{
return queue.Dequeue();
}
public int Count
{
get
{
return queue.Count;
}
}
}
And eventually test looks:
public async void TestMethod()
{
//arrange
var listToAdd = new List<object>() { new object() };
var queue = new QueueWrapper<object>();
var inMemoryQueue = new InMemoryQueue(queue);
//act
await inMemoryQueue.AddToAsync(listToAdd);
//assert
Assert.IsTrue(queue.Count == 1);
}
Upvotes: 1
Reputation: 236218
I think you can check if items which you added to queue was queued by receiving them back (actually this is expected behavior of your queue):
[Test]
public void Can_Receive_Added_Items()
{
//arrange
var listToAdd = new List<object> { new object() };
var inMemoryQueue = new InMemoryQueue();
//act
inMemoryQueue.AddToAsync(listToAdd).Wait();
//assert
var queuedItems = inMemoryQueue.ReceieveAsync(listToAdd.Count).Result;
CollectionAssert.AreEqual(listToAdd, queuedItems);
}
BTW Currently you are not adding items to result
list when dequeueing them, so your test will fail.
Upvotes: 3
Reputation: 669
I think the only way is to change the approach of your testing.
You will have to write your test from the process perspective.
First perform an Add, and then perform a Receive. You should have to assert the element you have received is the same that you have added.
If you want to ensure there was just one element added, perform a Receive again an ensure you get an empty list.
Upvotes: 0