【问题标题】:How to mock SerialDataReceivedEventArgs如何模拟 SerialDataReceivedEventArgs
【发布时间】:2011-11-26 18:44:12
【问题描述】:

我有一个使用串行端口与外围设备通信的外围驱动程序。我想对该驱动程序进行单元测试并尝试模拟串行端口。因此,我为框架类型 SerialPort 创建了一个包装器,让它实现一个接口:

public interface IScannerPort
{
    Handshake Handshake { get; set; }
    bool IsOpen { get; }

    event SerialDataReceivedEventHandler DataReceived;

    void Close( );
    void Open( );
    string ReadLine( );
}

现在我使用 moq 创建了一个模拟:

Mock<IScannerPort> scannerPort = new Mock<IScannerPort>( );

然后我想引发DataReceived 事件。但是 SerialDataReceivedEventArgs 不允许我设置 EventType 属性。所以我也尝试模拟 SerialDataReceivedEventArgs,结果是

Mock<SerialDataReceivedEventArgs> args = new Mock<SerialDataReceivedEventArgs>();
args.SetupProperty(a => a.EventType, SerialData.Eof);

但第二行引发了 NotSupportedException: Invalid setup on a non-virtual (VB 中可覆盖) 成员:a => a.EventType

如何发起活动?或者我如何模拟事件参数?

【问题讨论】:

    标签: c# unit-testing moq mstest


    【解决方案1】:

    我想我可能通过回答如何引发事件部分解决了你的问题,如下:

      var mockedScannerPort = new Mock<IScannerPort>();
    
                mockedScannerPort.Setup(sp => sp.ReadLine())
                                 .Returns("123")
                                 .Raises(sp => sp.DataReceived += null, new object[] { this, It.IsAny<SerialDataReceivedEventArgs>() });
    
                bool mockEventWasInvoked = false;
                mockedScannerPort.Object.DataReceived += (sedner, e) => mockEventWasInvoked = true;
                var retVal = mockedScannerPort.Object.ReadLine();
    
                Assert.AreEqual( "123", retVal );
                Assert.IsTrue(mockEventWasInvoked);
    

    Mocking SerialDataReceivedEventArgs 有点棘手,因为它没有默认构造函数。我会尽快尝试解决方法。

    编辑:感谢@PVitt 提供了存根 SerialDataEventArgs 的解决方法。为了完整起见,在此处发布此内容。

    [Test]
            public void Test()
            {
                var mockedScannerPort = new Mock<IScannerPort>();
    
                var constructor = typeof(SerialDataReceivedEventArgs).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(SerialData) }, null);
                var eventArgs = (SerialDataReceivedEventArgs)constructor.Invoke(new object[] { SerialData.Eof });
    
                mockedScannerPort.Setup(sp => sp.ReadLine())
                                 .Returns("123")
                                 .Raises(sp => sp.DataReceived += null, new object[] { this, eventArgs });
    
                bool mockEventWasInvoked = false;
                SerialDataReceivedEventArgs ea = null;
                mockedScannerPort.Object.DataReceived += (sedner, e) =>
                                                             {
                                                                 mockEventWasInvoked = true;
                                                                 ea = e;
                                                             };
    
                var retVal = mockedScannerPort.Object.ReadLine();
    
                Assert.AreEqual( "123", retVal );
                Assert.IsTrue(mockEventWasInvoked);
                Assert.IsNotNull( ea );
                Assert.AreEqual( ea.EventType, SerialData.Eof );
            }
    

    【讨论】:

    • 感谢您的回复,但我担心错过了关键点。事件处理程序依赖于无法模拟的 SerialDataReceivedEventArgs.EventType(好吧,我希望它可以 :))
    【解决方案2】:

    不一定要模拟 SerialDataReceivedEventArgs。可以使用反射创建它的一个实例:

    ConstructorInfo constructor = typeof (SerialDataReceivedEventArgs).GetConstructor(
        BindingFlags.NonPublic | BindingFlags.Instance,
        null,
        new[] {typeof (SerialData)},
        null);
    
    SerialDataReceivedEventArgs eventArgs = 
        (SerialDataReceivedEventArgs)constructor.Invoke(new object[] {SerialData.Eof});
    

    然后可以使用发泄参数的“真实”实例在模拟实例上引发事件:

    scannerPort.Raise( s => s.DataReceived += null, eventArgs );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-03
      • 2014-07-01
      • 1970-01-01
      相关资源
      最近更新 更多