unit testing system.timers.timer
Asked Answered
D

2

16

I've been reading over the questions about unit testing with timers and threading. I found the SO question about unit testing system.threading.timers, but I need to unit test a system.timers.timer and a wrapper class doesn't seem to work quite as smoothly for this one.

I just need to know how to mock the timer and/or system time in order to unit test against it. I can't seem to find this anywhere on google.

edit & update: It makes sense that if I extract the timer by wrapping it as below, I can generate a timer and use mocking to replace it with a different timer. The relevant part is then to take that timer that I'm injecting at runtime (the original, not a mock) and test it's elapsed event code.

Dragster answered 31/1, 2012 at 22:58 Comment(3)
It is hard to suggest something since you have not provided any information regarding which test case you want to cover and which code block represents a logic under the test case.Flite
You need to test the timer or test the code executed in each event? If so, you can put that logic in another class and just test that. You can trust the timer call it.Nmr
@ivowiblo: I don't know how to mark your comment as the answer, but I think that's the closest to the actual answer to the question as I can find.Dragster
I
27

What stops you from wrapping this one?

public interface ITimer
{
    void Start(double interval);
    void Stop();
    event ElapsedEventHandler Elapsed;
}

That's pretty much all your interface needs. Let's see how this could go (note that you could of course expose more Timer properties, but that's pretty much basic stuff that should be enough):

public class MyTimer : ITimer
{
    private Timer timer = new Timer();

    public void Start(double interval)
    {
        timer.Interval = interval; 
        timer.Start();
    }

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

    public event ElapsedEventHandler Elapsed
    {
        add { this.timer.Elapsed += value; }
        remove { this.timer.Elapsed -= value; }
    }
}

Now, how would you utilize this in your testing (assuming we're using FakeItEasy as mocking framework of choice):

var timerFake = A.Fake<ITimer>();
var classUnderTest = new MyClass(timerFake);

// tell fake object to raise event now
timerFake.Elapsed += Raise.With<ElapsedEventArgs>(ElapsedEventArgs.Empty).Now;

// assert whatever was supposed to happen as event response, indeed did
Assert.That(classUnderTest.ReceivedEvent, Is.True);

Example above actually does test the code that happens once the event on timer is raised. Consider MyClass looking like this:

public class MyClass
{
    private ITimer timer;

    public MyClass(ITimer timer)
    {
        this.timer = timer;
        this.timer.Elapsed += TimerElapsedHandler;
    }

    public bool ReceivedEvent { get; set; }

    private void TimerElapsedHandler(object sender, ElapsedEventArgs e)
    { 
        ReceivedEvent = true;
    }
}

In the test, we force timer to raise when we need it, and we check whether code in TimerElapsedHandler executed, by asserting ReceivedEvent property was set. In reality, this method might do more than that, but that will only change the way we do assertions - idea remains the same.


Edit: You can also try Moles, a framework that allows you to generate fakes of any framework types/methods. However, if mocking timer was all you wanted, I'd go with wrapper approach.

Inositol answered 31/1, 2012 at 23:10 Comment(5)
if I wrap it this way, how can I control the elapsed event and/or mock the object?Dragster
@deltree: what do you mean by how can you control event? If you wrap it this way, you can mock it like any other interface. I'll add simple example.Inositol
thank you for this. This is closer to what I actually said and clearly not what I meant to say. This is a great way to unit test that the timer is called, but not the code that's run when it's elapsed.Dragster
@deltree: this actually does test code that runs when timer elapsed - see my edit.Inositol
timerFake.Elapsed += Raise.With<ElapsedEventArgs>(ElapsedEventArgs.Empty).Now; results in a compiler error stating Argument type 'System.EventArgs' is not assignable to parameter type 'System.Timers.ElapsedEventArgs'. Is there an alternative?Trove
N
2

You need to test the timer or test the code executed in each event? If so, you can put that logic in another class and just test that. You can trust the timer will call it...

Nmr answered 31/1, 2012 at 23:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.