views:

269

answers:

7

Hi,

I am using C# 3.0 and NUnit. I'm wondering if there is a standard way to perform unit tests on code that executes after some amount of time. For example, I have a simple static class that I can register methods with, and have them be called n milliseconds later. I need to assure that code in the delegate methods is being called.

For example, the following test will always pass, because there are no assertions made until after the method has exited.

[Test]
public void SampleTest()
{
    IntervalManager.SetTimeout(delegate{ 
        Assert.Equals(now.Millisecond + 100, DateTime.Now.Millisecond); 
    }, 100);
}

Is it even possible to unit test code that doesn't execute immediately?

Cheers,

Paul

A: 

Of course you can test it. You just have to wait for it to execute.

John Saunders
+1  A: 

Yes, doing it like in your example will not work.

Rather, i'd recommend you create a test class, to be used as the delegate, that records when its method was called, and at what time.

You then inject your mock into the IntervalManager you want to test. Your test method then has to wait for the IntervalManager (using a suitable method provided by the IntervalManager, or just wait a few sec), then you can verify the state of the test class.

BTW, this approach is usually referred to as mocking; in this case the test class would be the mock object.

sleske
How do I wait a few seconds and verify the state of the test class? Use an old school style sleep() hack?
plemarquand
Well, just use the sleep() or equivalent function from your standard library (don't know the name in C#). And why would that be a hack? You're just waiting for the time the method is documented to require.
sleske
+1  A: 

So what exactly are you testing? Are you testing that Timers work? Or that your code properly sets up a Timer so that on expiration the timer executes a callback? Without knowing what the code looks like, I'm assuming that what you really want to test is the latter. My answer would be that (1) this is probably going to be hard with static methods and (2) you'll probably need to use dependency injection and inject the mock timers, etc. that don't actually run the resulting method but record via expectations that the proper calls were made by your code.

tvanfosson
+2  A: 

How about this? It causes the test to block for some expected maximum time for the callback to fire and complete before bailing, reporting an error.

public void Foo() {
    AutoResetEvent evt = new AutoResetEvent(false);
    Timer t = new Timer(state => {
     // Do work
     evt.Set();
    }, null, 100, Timeout.Infinite);
    if (evt.WaitOne(500)) {
     // method called and completed
    } else {
     // timed out waiting
    }
}
Andrew Arnott
Unit tests ought to be fast. Introducing timed waits into your tests will slow thing down -- 1/2 sec per test is way too slow if you have any appreciable number of tests like this. I suggest refactoring to a solution using mocks is a better way to handle this.
tvanfosson
The half-second is an arbitrary time that should be minimized, I agree. The principle of waiting for another thread to complete work during a unit test is still valid, whether that thread is on a timer or not. But I agree, timed waits during unit tests slow things down and should be avoided.
Andrew Arnott
+1  A: 

As a side note. We usually try to mark our slow running tests with a NUnit category and can choose to skip those tests on some builds.

Ola
A: 

Maybe I am missing something, but Visual Studio's unit testing has special attributes you can put on methods to control execution order and other things. This should have been automatically generated when you first made the unit testing project:

    #region Additional test attributes
 // 
 //You can use the following additional attributes as you write your tests:
 //
 //Use ClassInitialize to run code before running the first test in the class
 //[ClassInitialize]
 //public static void MyClassInitialize(TestContext testContext) {
 //}
 //
 //Use ClassCleanup to run code after all tests in a class have run
 //[ClassCleanup()]
 //public static void MyClassCleanup()
 //{
 //}
 //
 //Use TestInitialize to run code before running each test
 //[TestInitialize()]
 //public void MyTestInitialize()
 //{
 //}
 //
 //Use TestCleanup to run code after each test has run
 //[TestCleanup()]
 //public void MyTestCleanup()
 //{
 //}
 //
 #endregion

So using [ClassInitialize] should allow you to write whatever has to execute first into a method. Then your tests can run. Or you can use [TestInitialize] to run code before each test.

BluePlateSpecial
A: 

As a couple of other answers have indicated, long-running tests are generally a bad idea. To facilitate testing this component you should consider that you really have two distinct things that you're trying to test.

  1. When you register a timed delegate execution the proper time is set. This likely needs to be tested with various combinations of timeouts and numbers of delegates.
  2. The delegate is executed in the proper manner.

Separating the tests in the manner would allow you to test that your timing mechanism works as expected with a small number of short timeouts (testing all the cases you need to consider). Remember that you'll likely need a little bit of leeway in the actual time that it takes to execute a given delegate based on the current load on the system and how complex your component code is (that is in IntervalManager).

akmad