I am trying to unit test / verify that a method is being called on a dependency, by the system under test (SUT).
Unit Test (Moq):
[Test]
public void StartBar_ShouldCallStartOnAllFoo_WhenFoosExist()
{
//ARRANGE
//Create a foo, and setup expectation
var mockFoo0 = new Mock<IFoo>();
mockFoo0.Setup(foo => foo.Start());
var mockFoo1 = new Mock<IFoo>();
mockFoo1.Setup(foo => foo.Start());
//Add mockobjects to a collection
var foos = new List<IFoo>
{
mockFoo0.Object,
mockFoo1.Object
};
IBar sutBar = new Bar(foos);
//ACT
sutBar.Start(); //Should call mockFoo.Start()
//ASSERT
mockFoo0.VerifyAll();
mockFoo1.VerifyAll();
}
Implementation of IBar as Bar:
class Bar : IBar
{
private IEnumerable<IFoo> Foos { get; set; }
public Bar(IEnumerable<IFoo> foos)
{
Foos = foos;
}
public void Start()
{
foreach(var foo in Foos)
{
Task.Factory.StartNew(
() =>
{
foo.Start();
});
}
}
}
Moq Exception:
*Moq.MockVerificationException : The following setups were not matched:
IFoo foo => foo.Start() (StartBar_ShouldCallStartOnAllFoo_WhenFoosExist() in
FooBarTests.cs: line 19)*
@dpurrington & @StevenH: If we start putting this kind of stuff in our code
sut.Start();
Thread.Sleep(TimeSpan.FromSeconds(1));
and we have thousands of "unit" tests then our tests start running into the minutes instead of seconds. If you had for example 1000 unit tests, it will be hard to have your tests to run in under 5 seconds if someone has gone and littered the test code base with Thread.Sleep.
I suggest that this is bad practice, unless we are explicitly doing Integration testing.
My suggestion would be to use the System.Concurrency.IScheduler interface from System.CoreEx.dll and inject the TaskPoolScheduler implementation.
This is my suggestion for how this should be implemented
using System.Collections.Generic;
using System.Concurrency;
using Moq;
using NUnit.Framework;
namespace StackOverflowScratchPad
{
public interface IBar
{
void Start(IEnumerable<IFoo> foos);
}
public interface IFoo
{
void Start();
}
public class Bar : IBar
{
private readonly IScheduler _scheduler;
public Bar(IScheduler scheduler)
{
_scheduler = scheduler;
}
public void Start(IEnumerable<IFoo> foos)
{
foreach (var foo in foos)
{
var foo1 = foo; //Save to local copy, as to not access modified closure.
_scheduler.Schedule(foo1.Start);
}
}
}
[TestFixture]
public class MyTestClass
{
[Test]
public void StartBar_ShouldCallStartOnAllFoo_WhenFoosExist()
{
//ARRANGE
TestScheduler scheduler = new TestScheduler();
IBar sutBar = new Bar(scheduler);
//Create a foo, and setup expectation
var mockFoo0 = new Mock<IFoo>();
mockFoo0.Setup(foo => foo.Start());
var mockFoo1 = new Mock<IFoo>();
mockFoo1.Setup(foo => foo.Start());
//Add mockobjects to a collection
var foos = new List<IFoo>
{
mockFoo0.Object,
mockFoo1.Object
};
//ACT
sutBar.Start(foos); //Should call mockFoo.Start()
scheduler.Run();
//ASSERT
mockFoo0.VerifyAll();
mockFoo1.VerifyAll();
}
}
}
This now allows the test to run at full speed without any Thread.Sleep.
Note that the contracts have been modified to accept an IScheduler in the Bar constructor (for Dependency Injection) and the IEnumerable is now passed to the IBar.Start method. I hope this makes sense why I made these changes.
Speed of testing is the first and most obvious benefit of doing this. The second and possibly more important benefit of doing this is when you introduce more complex concurrency to your code, which makes testing notoriously difficult. The IScheduler interface and the TestScheduler can allow you to run deterministic "unit tests" even in the face of more complex concurrency.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With