Unit tests for COM events?

We have a home-made COM component written in C ++. Now we want to test its functions and events in the C # Test Project. Function tests are pretty simple. However, events never fire.

MyLib.MyClass m = new MyLib.MyClass(); Assert.IsTrue(m.doStuff()); // Works // This does not work. OnMyEvent is never called! m.MyEvent += new MyLib.IMyClassEvents_MyEventHandler(OnMyEvent); m.triggerEvent(); 

I searched for it and read about similar issues here in StackOverflow. I tried all the suggested methods, but can't get it to work!

So far, I have tried to run my test using the active dispatcher , but without success. I also tried manually pumping messages to the main thread using Dispatcher.PushFrame() . Nothing. My events never fire. I created a simple WinForms project and confirmed that my events work in normal setup. Therefore, this problem applies only to Unit Tests.

Q: How to create a normal C # Unit Test that can successfully run active event handlers?

Someone out there must have a working sample! Please, help.

+7
source share
1 answer

If your COM object is an STA, you probably need to start a message loop so that its events light up.

To do this, you can use a small wrapper around the Application and Form objects. Here is a small example that I wrote in a few minutes.

Please note that I have not run and tested it, so it may not work, and cleaning should be better. But this may give you direction to decide.

Using this approach, the test class will look something like this:

 [TestMethod] public void Test() { MessageLoopTestRunner.Run( // the logic of the test that should run on top of a message loop runner => { var myObject = new ComObject(); myObject.MyEvent += (source, args) => { Assert.AreEqual(5, args.Value); // tell the runner we don't need the message loop anymore runner.Finish(); }; myObject.TriggerEvent(5); }, // timeout to terminate message loop if test doesn't finish TimeSpan.FromSeconds(3)); } 

And the code for MessageLoopTestRunner would be something like this:

 public interface IMessageLoopTestRunner { void Finish(); } public class MessageLoopTestRunner : Form, IMessageLoopTestRunner { public static void Run(Action<IMessageLoopTestRunner> test, TimeSpan timeout) { Application.Run(new MessageLoopTestRunner(test, timeout)); } private readonly Action<IMessageLoopTestRunner> test; private readonly Timer timeoutTimer; private MessageLoopTestRunner(Action<IMessageLoopTestRunner> test, TimeSpan timeout) { this.test = test; this.timeoutTimer = new Timer { Interval = (int)timeout.TotalMilliseconds, Enabled = true }; this.timeoutTimer.Tick += delegate { this.Timeout(); }; } protected override void OnLoad(EventArgs e) { base.OnLoad(e); // queue execution of the test on the message queue this.BeginInvoke(new MethodInvoker(() => this.test(this))); } private void Timeout() { this.Finish(); throw new Exception("Test timed out."); } public void Finish() { this.timeoutTimer.Dispose(); this.Close(); } } 

Does it help?

+1
source

All Articles