C# 单元测试使用计时器的类
我有一个类,它有一个私有成员,类型为System.Windows.Forms.Timer。还有一个私有方法,每次我的计时器滴答作响时都会调用它 这种方法值得测试吗?因为它是私人的 我如何测试它?我知道我可以让我的测试类继承我想要测试的类。。。 我应该嘲笑我的计时器吗?因为如果我必须测试一个使用内部计时器的类,我的测试可能需要很多时间才能完成,对吗? 编辑: 实际上,该方法依赖于时间,下面是代码:C# 单元测试使用计时器的类,c#,unit-testing,tdd,C#,Unit Testing,Tdd,我有一个类,它有一个私有成员,类型为System.Windows.Forms.Timer。还有一个私有方法,每次我的计时器滴答作响时都会调用它 这种方法值得测试吗?因为它是私人的 我如何测试它?我知道我可以让我的测试类继承我想要测试的类。。。 我应该嘲笑我的计时器吗?因为如果我必须测试一个使用内部计时器的类,我的测试可能需要很多时间才能完成,对吗? 编辑: 实际上,该方法依赖于时间,下面是代码: private void alertTick(object sender, EventArgs e)
private void alertTick(object sender, EventArgs e) {
if (getRemainingTime().Seconds <= 0) {
Display.execute(Name, WarningState.Ending, null);
AlertTimer.Stop();
}
else {
var warning = _warnings.First(x => x == getRemainingTime());
if (warning.TotalSeconds > 0)
Display.execute(Name, WarningState.Running, warning);
}
}
如您所见,如果计时器正在运行,它将调用Display.execute,参数与剩余时间等于0时不同。这是设计的问题吗
这种方法值得测试吗?因为它是私人的
您的目的是决定代码是否有效。即使它是一个私有方法,它也应该生成一个公共接口可以访问的输出。您应该以用户能够知道类是否工作的方式来设计类
此外,在进行单元测试时,如果可以模拟计时器,则可以访问分配给计时器的已用事件的回调
我如何测试它?我知道我可以让我的测试类继承
我想测试的类
您可以在这里使用适配器类。首先,您必须定义一个抽象,因为Timer类不提供抽象
public interface ITimer
{
void Start();
void Stop();
double Interval { get; set; }
event ElapsedEventHandler Elapsed;
//and other members you need
}
然后可以在适配器类中实现该接口,只需从Timer类继承即可
public class TimerAdaper : Timer, ITimer { }
您应该将抽象作为属性注入构造函数中,以便在测试中模拟它
public class MyClass
{
private readonly ITimer _timer;
public MyClass(ITimer timer)
{
_timer = timer
}
}
我应该嘲笑我的计时器吗?因为如果我要测试一个类
使用内部计时器,我的测试可能需要很多时间才能完成,
对吧?
当然,你应该嘲笑你的计时器。单元测试不能依赖于系统时间。您应该通过模拟来引发事件,并查看代码的行为
您不是在测试私有或公共方法,而是在验证类的行为。如果您还没有验证某些行为,则无法判断它是否已实现。有几种方法可以调用此行为—类的公共接口或依赖项中的某些事件。行为调用也不一定会改变公共接口所达到的效果,与依赖项的交互也很重要。
请参见下面的示例-它显示了如何测试此类隐藏行为。
请参见下面的示例-它展示了如何划分职责、注入依赖项和模拟它们。
实际上,您的类有太多的职责——一个是安排一些任务,另一个是执行一些操作。试着把你的班级分成两个独立的班级
因此,调度将转到调度程序:调度程序的API可能类似于:
public interface IScheduler
{
event EventHandler<SchedulerEventArgs> Alarm;
void Start();
void Stop();
}
考试通过了。再写一篇:
[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));
}
您可以继续为类编写测试,该类负责处理调度程序警报并在显示时执行一些警告。完成后,可以为isScheduler接口编写实现。无论您将如何实现调度—通过System.Windows.Forms.Timer或通过System.ThreadingTimer或其他方式。您想验证什么行为?实际方法本身是否依赖于时间?单元测试应该测试方法的功能;定期调用该方法的事实不应改变您想要测试该方法是否有效的事实。我想说,除非方法的成功取决于时间,否则绝对没有必要模仿计时器。当涉及到测试时,方法的可访问性并不重要;这是你程序的一个功能部分。+1因为我在私人领域测试中输入了完全相同的问题,所以你在时间上击败了我。可能重复的值得测试的问题是你可以更好地处理的。这有风险吗?它会失败吗?一般的准则是通过使用私有方法的公共方法来测试私有方法。最后,如果时间方面/角色使测试变得困难/不可预测,那么您应该模拟它。请您详细说明私人是否值得测试,然后如何测试them@HatSoft如果一个类的公共接口不能访问一段公共或私有逻辑,那么代码就不能使用。因此,我们应该始终能够测试类中的所有代码。@HatSoft即使附加到计时器已用事件的代码是私有的,它的启动部分也应该在某些公共成员构造函数中。这样就可以访问私有部分。如果没有上下文,我不能说你如何测试它,但你应该也可以测试它。@dtryon&Ufuk谢谢你,你能分享一些关于如何测试private的链接吗s@HatSoft这不是直接测试private的逻辑。它是关于找到执行privates功能的公共流,然后制作一个使用该流的测试。我们如何为调度器实现编写单元测试@偷简单积分 毫无疑问:
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]
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));
}
private void Scheduler_Alarm(object sender, SchedulerEventArgs e)
{
if (e.RemainingTime > 0)
return;
_display.Execute(_name, WarningState.Ending, null);
_scheduler.Stop();
}