My company is finally starting to introduce unit tests. While going through some of the new tests, I came across this one:
[Test]
public void GetCorrectTimeoutInfoFromTimeSpan()
{
// Arrange
bool preTimeout;
bool postTimeout;
double random = new Random().NextDouble();
TimeoutHandler timeoutHandler;
// Act
timeoutHandler = new TimeoutHandler(TimeSpan.FromMilliseconds(1));
preTimeout = timeoutHandler.IsTimeout;
while(!timeoutHandler.IsTimeout)
{
// Simulate some work
random = Math.Sin(random) * Math.Cos(random);
}
GC.KeepAlive(random);
postTimeout = timeoutHandler.IsTimeout;
// Assert
Assert.Multiple(() =>
{
Assert.That(preTimeout, Is.False);
Assert.That(postTimeout, Is.True);
});
}
The variable random is only there to simulate some work, ensured to not be "optimized" away via the GC.KeepAlive(). Since this is will be reused in many different situations where we need to simulate work, I wanted to see if there is a better/cleaner way.
Off the top of my head I would think something like this might work
{...}
double? rnd = null;
while(!timeoutHandler.IsTimeout)
rnd = SimulateWork(rnd);
{...}
public static double SimulateWork(double? rnd = null)
{
rnd ??= new Random().NextDouble();
return Math.Sin(rnd.Value) * Math.Cos(rnd.Value);
}
This would seperate the dependency for random values and as far as I know C# should keep this code as is, making the specific call to garbage collector not needed.
As it still requires a local variable, I am not sure if this is truly better or I am just overthinking things.
Thread.Sleep(). What is the purpose of that?new Random()all the time also kind of raises a red flag. That's really not a cheap thing to do. The more UnitTests you'll have, the more this will become significant to overall tests execution time.GC.KeepAlive(random)has no effect and will just confuse future readers.TimeoutHandlershould injectITimeProvider. In a unit test you would then give it aFakeTimeProviderand manipulate the time by code instead of "simulating work".