C# 用Rhino Mocks模拟阻塞调用

C# 用Rhino Mocks模拟阻塞调用,c#,multithreading,unit-testing,rhino-mocks,blocking,C#,Multithreading,Unit Testing,Rhino Mocks,Blocking,我目前正在使用TDD构建一个类。该类负责等待特定窗口变为活动状态,然后触发某些方法 我正在使用AutoIt COM库(有关AutoIt外观的更多信息),因为我想要的行为实际上是AutoIt中的一个方法 代码大致如下所示: public class WindowMonitor { private readonly IAutoItX3 _autoItLib; public WindowMonitor(IAutoItX3 autoItLib) { _autoI

我目前正在使用TDD构建一个类。该类负责等待特定窗口变为活动状态,然后触发某些方法

我正在使用AutoIt COM库(有关AutoIt外观的更多信息),因为我想要的行为实际上是AutoIt中的一个方法

代码大致如下所示:

public class WindowMonitor
{
    private readonly IAutoItX3 _autoItLib;

    public WindowMonitor(IAutoItX3 autoItLib)
    {
        _autoItLib = autoItLib;
    }


    public void Run() // indefinitely
    {
        while(true)
        {
            _autoItLib.WinWaitActive("Open File", "", 0);
            // Do stuff now that the window named "Open File" is finally active.
        }
    }
}
正如您所见,AutoIt COM库实现了一个我可以模拟的接口(使用NUnit和Rhino模拟):

[TestFixture]
运行监视器时的公共类
{
视窗监控主体;
IAutoItX3模拟自动图书馆;
自动存储事件持续处于活动状态;
调用AutoResetEvent WinWaitActives;
[设置]
公共作废设置()
{
//安排
mockAutoItLibrary=MockRepository.GenerateSub();
mockAutoItLib.Stub(m=>m.WinWaitActive(“,”,0))
.IgnoreArguments()
.Do((Func)((a,b,c)=>
{
winWaitActiveIsCalled.Set();
continueWinWaitActive.WaitOne();
返回1;
}));
主题=新主题(mockAutoItLibrary)
//表演
新线程(新线程开始(subject.Run)).Start();
winWaitActiveIsCalled.WaitOne();
}
//断言
[测试]
[超时(1000)]
public void应调用
{
调用mockAutoItLib.Assertwas(m=>m.WinWaitActive(“Bestand selecteren”,0));
}
[测试]
[超时(1000)]
public void确保当窗口处于非活动状态时,什么都没有做()
{
//当您为窗口激活时的操作执行“AssertWasCalled”时,请在此处放置等效的“AssertWasNotCalled”。
}
}

问题是,第一次测试总是超时。我已经发现,当调用存根“WinWaitActive”时,它会阻塞(如预期的那样,在单独的线程上),而当在这之后调用“assertwascall”时,执行永远不会返回

我不知道如何继续,也找不到模拟阻塞调用的任何示例

因此,总而言之:

有没有办法模拟阻塞调用而不使测试超时


(注意,我对更改设计不太感兴趣(即“不要使用阻塞调用”)因为在这里可以做到这一点,但我确信在某些情况下,改变设计要困难得多,而且我对更通用的解决方案感兴趣。但是如果根本不可能模拟阻塞调用,那么像这样的建议更受欢迎!)

不确定我是否理解这个问题

您的代码只是在mock(
WinWaitActive
)上调用一个方法。当然,在呼叫返回之前,它无法继续。这是编程语言的本质,不需要测试

因此,如果您测试调用了
WinWaitActive
,测试就完成了。您可以先测试
WinWaitActive
是否被调用,但这需要有序的期望,这需要老式的rhino mocks语法,通常不值得这么做

   mockAutoItLibrary = MockRepository.GenerateStub<IAutoItX3>();

   subject = new Subject(mockAutoItLibrary)
   subject.Run()

   mockAutoItLib.AssertWasCalled(m => m.WinWaitActive("Open File", "", 0));
mockAutoItLibrary=MockRepository.generateSub();
主题=新主题(mockAutoItLibrary)
subject.Run()
调用mockAutoItLib.assertwas(m=>m.WinWaitActive(“打开文件”,“0”);
你不做任何其他事情然后调用一个方法。。。所以没有其他的测试了

编辑:退出无限循环

您可以通过从模拟中抛出异常使其退出无限循环。这不是很好,但是它避免了在单元测试中使用所有这些多线程的东西

   mockAutoItLibrary = MockRepository.GenerateStub<IAutoItX3>();

   // make loop throw an exception on second call
   // to exit the infinite loop
   mockAutoItLib
     .Stub(m => m.WinWaitActive(
       Arg<string>.Is.Anything, 
       Arg<string>.Is.Anything, 
       Arg<int>.Is.Anything));
     .Repeat.Once();

   mockAutoItLib
     .Stub(m => m.WinWaitActive(
       Arg<string>.Is.Anything, 
       Arg<string>.Is.Anything, 
       Arg<int>.Is.Anything));
     .Throw(new StopInfiniteLoopException());

   subject = new Subject(mockAutoItLibrary)
   try
   {
     subject.Run()
   }
   catch(StopInfiniteLoopException)
   {} // expected exception thrown by mock

   mockAutoItLib.AssertWasCalled(m => m.WinWaitActive("Open File", "", 0));
mockAutoItLibrary=MockRepository.generateSub();
//使循环在第二次调用时引发异常
//退出无限循环
mockAutoItLib
.Stub(m=>m.WinWaitActive(
Arg.Is.任何东西,
Arg.Is.任何东西,
参数是任何东西);
.重复一次();
mockAutoItLib
.Stub(m=>m.WinWaitActive(
Arg.Is.任何东西,
Arg.Is.任何东西,
参数是任何东西);
.Throw(new stopInfiniteLopeException());
主题=新主题(mockAutoItLibrary)
尝试
{
subject.Run()
}
catch(stopInfiniteLopeException)
{}//预期模拟引发异常
调用mockAutoItLib.assertwas(m=>m.WinWaitActive(“打开文件”,“0”);

不确定我是否理解这个问题

您的代码只是在mock(
WinWaitActive
)上调用一个方法。当然,在呼叫返回之前,它无法继续。这是编程语言的本质,不需要测试

因此,如果您测试调用了
WinWaitActive
,测试就完成了。您可以先测试
WinWaitActive
是否被调用,但这需要有序的期望,这需要老式的rhino mocks语法,通常不值得这么做

   mockAutoItLibrary = MockRepository.GenerateStub<IAutoItX3>();

   subject = new Subject(mockAutoItLibrary)
   subject.Run()

   mockAutoItLib.AssertWasCalled(m => m.WinWaitActive("Open File", "", 0));
mockAutoItLibrary=MockRepository.generateSub();
主题=新主题(mockAutoItLibrary)
subject.Run()
调用mockAutoItLib.assertwas(m=>m.WinWaitActive(“打开文件”,“0”);
你不做任何其他事情然后调用一个方法。。。所以没有其他的测试了

编辑:退出无限循环

您可以通过从模拟中抛出异常使其退出无限循环。这不是很好,但是它避免了在单元测试中使用所有这些多线程的东西

   mockAutoItLibrary = MockRepository.GenerateStub<IAutoItX3>();

   // make loop throw an exception on second call
   // to exit the infinite loop
   mockAutoItLib
     .Stub(m => m.WinWaitActive(
       Arg<string>.Is.Anything, 
       Arg<string>.Is.Anything, 
       Arg<int>.Is.Anything));
     .Repeat.Once();

   mockAutoItLib
     .Stub(m => m.WinWaitActive(
       Arg<string>.Is.Anything, 
       Arg<string>.Is.Anything, 
       Arg<int>.Is.Anything));
     .Throw(new StopInfiniteLoopException());

   subject = new Subject(mockAutoItLibrary)
   try
   {
     subject.Run()
   }
   catch(StopInfiniteLoopException)
   {} // expected exception thrown by mock

   mockAutoItLib.AssertWasCalled(m => m.WinWaitActive("Open File", "", 0));
mockAutoItLibrary=MockRepository.generateSub();
//使循环在第二次调用时引发异常
//退出无限循环
mockAutoItLib
.Stub(m=>m.WinWaitActive(
Arg.Is.任何东西,
Arg.Is.任何东西,
参数是任何东西);
.重复一次();
mockAutoItLib
.Stub(m=>m.WinWaitActive(
Arg.Is.任何东西,
Arg.Is.任何东西,
参数是任何东西);
.Throw(new stopInfiniteLopeException());
主题=新主题(mockAutoItLibrary)
尝试
{
subject.Run()
}
catch(stopInfiniteLopeException)
{}//expe