为没有虚方法的类创建一个Mock

时间:2015-06-22 15:42:44

标签: c# unit-testing mocking

我有一个使用.NETS SerialPort类与设备通信的类。

我想使用Mocks帮助为我的班级编写测试,理想情况下我想为SerialPort类创建一个Mock。

我相信我无法创建SerialPort的模拟,因为该类没有任何关键方法,这是正确的吗?

为了解决这个限制,我创建了一个接口和一个实现MyComPort。使用这个我能够实现一些测试。但是,我想避免需要MyComPort课程。这可能吗?

IComPort

我将用来制作模拟的界面。

public interface IComPort{   
    int ReadTimeout { get; set; }   
    void Open();    
    void Write(String data);    
    String ReadExisting();    
    int ReadChar();    
    void DiscardInBuffer();
}

MyComPort

用于包装.NET SerialPort并实现IComPort接口的类。

我想避免需要这门课。如果不可能,我想知道制作所有方法AgressiveInlining是否是个好主意。

public class MyComPort : SerialPort, IComPort{

    public new int ReadTimeout {
        get { return base.ReadTimeout; }
        set { base.ReadTimeout = value; }
    }

    public MotorComPort(String portName, int baudrate, Parity parity, int databits, StopBits stopbits)
        : base(portName, baudrate, parity, databits, stopbits) { }

    public new void Open() {
        base.Open();
    }

    public new void Write(String data) {
        base.Write(data);
    }

    public new String ReadExisting() {
        return base.ReadExisting();
    }

    public new int ReadChar() {
        return base.ReadChar();
    }

    public new void DiscardInBuffer() {
        base.DiscardInBuffer();
    }
}

ClassThatUsesComPort

使用串口的类,我想测试的那个。

public class ClassThatUsesComPort{

    private IComPort mComPort;

    public ClassThatUsesComPort(IComPort cP){
        mComPort = cP;
    }

    [...]
}

主要

我正在使用我想要测试的类的主要例子。

class Program{
    static void Main(...){
        MyComPort mcp = new MyComPort(...);
        ClassThatUsesComPort x = new ClassThatUsesComPort(mcp);
        [...]
    }
}

1 个答案:

答案 0 :(得分:2)

您通过在应用程序和串行端口之间创建隔离点来实现正确的方法,以便您可以测试类逻辑。您还可以正确使用Moq来模拟SerialPort类。正如在评论中所说的那样,有一些模拟框架,比如TypeMock,可以让你直接嘲笑SerialPort然而它们往往很昂贵而且因为它们所以如果你使用像Moq这样更传统的模拟框架,那么你不会总是获得强大的代码结构优势。

就个人而言,我赞成SerialPort SerialPort,而不是继承它。我认为它更清楚地表明了您所暴露的public class MySerialPort: IComPort, IDisposable { SerialPort _serialPort; public MotorComPort(String portName, int baudrate, Parity parity, int databits, StopBits stopbits) { _serialPort = new SerialPort(portName, baudrate, parity, databits, stopbits); } // ... } 课程中的哪些行为。通过继承,您尚未覆盖的所有现有行为都可供您的应用程序使用,并且您在没有先将这些方法添加到界面的情况下调用这些方法的可能性更高。所以,你的课程看起来更像是这样:

IComPort

至于#34; AggressiveInlining"并且,将要生成的包装器代码应该很小,并且可能是编译器无论如何都要内联的候选者。但是,您应该通过您已声明{{1}}的界面调用方法。由于您是通过界面进行的,因此编译器不会内联它们(它根本不知道您总是会在特定的具体类上调用该方法)。如果编译器确实内联了具体的实现,那么你就不能为你的类提供一个模拟接口,以实现不同的行为。