- You are not testing methods (private or public) - you are verifying behavior of your class. And if you have not verified some behavior, then you can't tell it was implemented. There are several ways this behavior could be invoked - public interface of your class, or some event in dependency. Also not necessary that behavior invocation will change something reached by the public interface, interactions with dependencies also matter.
- See example below - it shows how to test such "hidden" behavior.
- See example below - it shows how to split responsibilities, inject dependencies and mock them.
Actually your class have too many responsibilities - one is scheduling some task, and another - executing some actions. Try to split your class into two separate classes with single responsibilities.
So, scheduling goes to scheduler :) API of scheduler could be like:
public interface IScheduler
{
event EventHandler<SchedulerEventArgs> Alarm;
void Start();
void Stop();
}
Forget about scheduler for now. Return and implement your second class, which will display some warnings. Let's go test first (with Moq):
[Test]
public void ShouldStopDisplayingWarningsWhenTimeIsOut()
{
Mock<IDisplay> display = new Mock<IDisplay>();
Mock<IScheduler> scheduler = new Mock<IScheduler>();
Foo foo = new Foo("Bar", scheduler.Object, display.Object);
scheduler.Raise(s => s.Alarm += null, new SchedulerEventArgs(0));
display.Verify(d => d.Execute("Bar", WarningState.Ending, null));
scheduler.Verify(s => s.Stop());
}
Write implementation:
public class Foo
{
private readonly IScheduler _scheduler;
private readonly IDisplay _display;
private readonly string _name;
public Foo(string name, IScheduler scheduler, IDisplay display)
{
_name = name;
_display = display;
_scheduler = scheduler;
_scheduler.Alarm += Scheduler_Alarm;
_scheduler.Start();
}
private void Scheduler_Alarm(object sender, SchedulerEventArgs e)
{
_display.Execute(_name, WarningState.Ending, null);
_scheduler.Stop();
}
}
Test passes. Write another one:
[Test]
public void ShouldNotStopDisplayingWarningsWhenTimeRemains()
{
Mock<IDisplay> display = new Mock<IDisplay>(MockBehavior.Strict);
Mock<IScheduler> scheduler = new Mock<IScheduler>(MockBehavior.Strict);
scheduler.Setup(s => s.Start());
Foo foo = new Foo("Bar", scheduler.Object, display.Object);
scheduler.Raise(s => s.Alarm += null, new SchedulerEventArgs(1));
}
Test failed. Ah, you need condition for remaining time:
private void Scheduler_Alarm(object sender, SchedulerEventArgs e)
{
if (e.RemainingTime > 0)
return;
_display.Execute(_name, WarningState.Ending, null);
_scheduler.Stop();
}
You can continue writing tests for your class, which responsible for handling scheduler alerts and executing some warnings on display. When you finish, you can write implementation for your IScheduler
interface. It does not matter how you will implement scheduling - via System.Windows.Forms.Timer or via System.ThreadingTimer, or some other way.