如何运行分配给模拟的事件处理程序? [英] How can I run the event handler assigned to a mock?

查看:15
本文介绍了如何运行分配给模拟的事件处理程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试触发分配给我的计时器模拟的事件处理程序.我如何在这里测试这个私有方法?

I am trying to fire the event handler assigned to my timer mock. How can I test this private method here?

public interface ITimer
{
    void Start();
    double Interval { get; set; }
    event ElapsedEventHandler Elapsed;
}

客户端类为该对象分配一个事件处理程序.我想测试这个类的逻辑.

Client class assigns an event handler to this object. I want to test the logic in this class.

_timer.Elapsed += ResetExpiredCounters;

并且分配的方法是私有的

And the assigned method is private

private void ResetExpiredCounters(object sender, ElapsedEventArgs e)
{
    // do something
}

我想在我的模拟中拥有这个事件处理程序并以某种方式运行它.我该怎么做?

I want to have this event handler in my mock and run it somehow. How can I do this?

更新:

我意识到我在分配事件处理程序之前引发了事件.我更正了,但我仍然收到此错误:

I realized I was raising the event before I assigned the event handler. I corrected that but I still get this error:

System.ArgumentException : Object of type 'System.EventArgs' cannot be converted 
to type 'System.Timers.ElapsedEventArgs'.

我是这样举的:

_timer.Raise(item => item.Elapsed += null, ElapsedEventArgs.Empty);

_timer.Raise(item => item.Elapsed += null, EventArgs.Empty);

两者都行不通.

更新:

这是对我有用的东西.请注意,如果您尝试将信息传递给 Jon 在评论中指出的事件处理程序,则它没有用.我只是用它来模拟 System.Timers.Timer 类的包装器.

Here's the thing that worked for me. Note that it's not useful if you are trying to pass info to event handler like Jon pointed out in comments. I am just using it to mock the wrapper for System.Timers.Timer class.

_timer.Raise(item => item.Elapsed += null, new EventArgs() as ElapsedEventArgs);

最后,如果您需要使用事件参数,这将毫无帮助,因为它始终为空.但是,这是唯一的方法,因为 ElapsedEventArgs 只有一个内部构造函数.

In the end, this won't help at all if you need to use event arguments since it will be always null. However, it's the only way since ElapsedEventArgs has only an internal constructor.

推荐答案

ElapsedEventArgs 有私有构造函数,不能实例化.

ElapsedEventArgs has a private constructor and can not be instantiated.

如果你使用:

timer.Raise(item => item.Elapsed += null, new EventArgs() as ElapsedEventArgs);

然后处理程序将接收一个空参数并丢失其 SignalTime 属性:

Then the handler will recevie a null parameter and lose its SignalTime property:

private void WhenTimerElapsed(object sender, ElapsedEventArgs e)
{
    // e is null.
}

在某些情况下您可能需要此参数.

You might want this parameter in some cases.

为了解决这个问题并使其更具可测试性,我还为 ElapsedEventArgs 创建了一个包装器,并让界面使用它:

To solve this and make it more testable, I also created a wrapper for the ElapsedEventArgs, and made the interface use it:

public class TimeElapsedEventArgs : EventArgs
{
    public DateTime SignalTime { get; private set; }

    public TimeElapsedEventArgs() : this(DateTime.Now)
    {
    }

    public TimeElapsedEventArgs(DateTime signalTime)
    {
        this.SignalTime = signalTime;
    }
}

public interface IGenericTimer : IDisposable
{
    double IntervalInMilliseconds { get; set; }

    event EventHandler<TimerElapsedEventArgs> Elapsed;

    void StartTimer();

    void StopTimer();
}

实现将简单地触发它自己的事件,从真正的计时器事件中获取数据:

The implementation will simply fire its own event getting the data from the real timer event:

public class TimerWrapper : IGenericTimer
{
    private readonly System.Timers.Timer timer;

    public event EventHandler<TimerElapsedEventArgs> Elapsed;

    public TimeSpan Interval
    {
        get
        {
            return this.timer.Interval;
        }
        set
        {
            this.timer.Interval = value;
        }
    }

    public TimerWrapper (TimeSpan interval)
    {
        this.timer = new System.Timers.Timer(interval.TotalMilliseconds) { Enabled = false };
        this.timer.Elapsed += this.WhenTimerElapsed;
    }

    private void WhenTimerElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
    {
        var handler = this.Elapsed;
        if (handler != null)
        {
            handler(this, new TimeElapsedEventArgs(elapsedEventArgs.SignalTime));
        }
    }

    public void StartTimer()
    {
        this.timer.Start();
    }

    public void StopTimer()
    {
        this.timer.Stop();
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                this.timer.Elapsed -= this.WhenTimerElapsed;
                this.timer.Dispose();
            }

            this.disposed = true;
        }
    }
}

现在,您可以简化和改进此事件的模拟:

Now, you can simplify and improve the mock of this event:

timer.Raise(item => item.Elapsed += null, new TimeElapsedEventArgs());

var yesterday = DateTime.Now.AddDays(-1);
timer.Raise(item => item.Elapsed += null, new TimeElapsedEventArgs(yesterday));

编写的代码更少,更易于使用,并且与框架完全解耦.

Less code to write, easier to work with and completely decoupled from the framework.

这篇关于如何运行分配给模拟的事件处理程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆