C# 如何在单元测试中为被测系统设置调试?

C# 如何在单元测试中为被测系统设置调试?,c#,.net,visual-studio,unit-testing,moq,C#,.net,Visual Studio,Unit Testing,Moq,如何设置调试,以便在测试代码下逐步完成系统? 我有一个测试: [Test] public void GetDeviceSettings_is_called() { //Arrange var mockDeviceInteractions = new Mock<IDeviceInteractions>(); mockDeviceInteractions.SetupSet(p => p._settingsFil

如何设置调试,以便在测试代码下逐步完成系统?

我有一个测试:

    [Test]
    public void GetDeviceSettings_is_called()
    {
        //Arrange
        var mockDeviceInteractions = new Mock<IDeviceInteractions>();
        mockDeviceInteractions.SetupSet(p => p._settingsFileQueue = It.IsAny<string>());
        mockDeviceInteractions.Setup(x => x.GetDeviceSettings(It.IsAny<string>(), It.IsAny<string>()));
        //Act
        mockDeviceInteractions.Object._settingsFileQueue = @"C:\";
        mockDeviceInteractions.Object.GetDeviceSettings("123123", "dfgdfg");
        //Assert
        mockDeviceInteractions.VerifyAll();
    }
我做错了什么?为什么它不在断点处停止?

您必须模拟被测试类调用的对象,而不是类本身

这里有一个例子。它假设您可以将依赖项注入到类中

想象你正在写一个核控制系统。您希望通过编写单元测试来检查系统是否会在需要时指示反应堆执行紧急停堆

public enum ReactorStatus { Ok, OhDear};

public interface IReactorInteractions {
    ReactorStatus ShutDown(bool nicely);
    ReactorStatus Status { get; }
}

public interface ICore {
    ReactorStatus ShutDown(bool nicely);
    ReactorStatus GetStatus();
}

public class ReactorInteractions : IReactorInteractions {

    private ICore reactor;
    public ReactorInteractions(ICore reactor) {
         this.reactor = reactor;
    }

    public ReactorStatus ShutDown(bool nicely) {
          return reactor.ShutDown(nicely);
    }

    public ReactorStatus Status { 
     get { return reactor.GetStatus(); } 
    }
}
因此,您需要测试ReactorInteractions类

为了做到这一点,然后模拟它调用的对象,在本例中是
ICore
您不想在实际内核上执行操作。这样做肯定会违反纪律,至少是这样

您应该将
ICore
mock的
Object
属性作为构造函数参数传递给ReactorInteractions类-此属性不是您应该在测试中访问的属性,它仅被设计为传递给被测试的类-被测试的类作用于此“对象”,允许您使用
设置
验证

private Mock<ICore> mockCore;
private IReactorInteractions reactor;

[SetUp]
public void TestSetup() {
    mockCore = new Mock<ICore>();
    reactor = new ReactorInteractions(mockCore.Object);
}
请注意,我没有在测试设置中使用
It.IsAny
。对于刚接触mocking的开发人员来说,这种语法非常混乱(在野外看到:尝试在测试调用中使用
It.IsAny
作为参数),我希望Moq作者在文档中强调这一点It.IsAny仅在您完全无法控制参数时使用。

在您的情况下,无需使用
它。我有任何
-您确切地知道要传递的值:

然后在测试中,检查是否使用了实际值。如果您测试
It.IsAny
,请记住,您的代码中处理该值的部分可以替换为随机数生成器,单元测试仍然可以通过

话虽如此,Moq有一个限制,即如果列表中的一个参数未知,必须使用
它。IsAny
那么它们都必须使用(我不再使用它了,所以我不知道是否仍然如此,但我似乎记得您可以通过使用回调手动验证参数来解决这个问题)

您必须模拟被测试类调用的对象,而不是类本身

这里有一个例子。它假设您可以将依赖项注入到类中

想象你正在写一个核控制系统。您希望通过编写单元测试来检查系统是否会在需要时指示反应堆执行紧急停堆

public enum ReactorStatus { Ok, OhDear};

public interface IReactorInteractions {
    ReactorStatus ShutDown(bool nicely);
    ReactorStatus Status { get; }
}

public interface ICore {
    ReactorStatus ShutDown(bool nicely);
    ReactorStatus GetStatus();
}

public class ReactorInteractions : IReactorInteractions {

    private ICore reactor;
    public ReactorInteractions(ICore reactor) {
         this.reactor = reactor;
    }

    public ReactorStatus ShutDown(bool nicely) {
          return reactor.ShutDown(nicely);
    }

    public ReactorStatus Status { 
     get { return reactor.GetStatus(); } 
    }
}
因此,您需要测试ReactorInteractions类

为了做到这一点,然后模拟它调用的对象,在本例中是
ICore
您不想在实际内核上执行操作。这样做肯定会违反纪律,至少是这样

您应该将
ICore
mock的
Object
属性作为构造函数参数传递给ReactorInteractions类-此属性不是您应该在测试中访问的属性,它仅被设计为传递给被测试的类-被测试的类作用于此“对象”,允许您使用
设置
验证

private Mock<ICore> mockCore;
private IReactorInteractions reactor;

[SetUp]
public void TestSetup() {
    mockCore = new Mock<ICore>();
    reactor = new ReactorInteractions(mockCore.Object);
}
请注意,我没有在测试设置中使用
It.IsAny
。对于刚接触mocking的开发人员来说,这种语法非常混乱(在野外看到:尝试在测试调用中使用
It.IsAny
作为参数),我希望Moq作者在文档中强调这一点It.IsAny仅在您完全无法控制参数时使用。

在您的情况下,无需使用
它。我有任何
-您确切地知道要传递的值:

然后在测试中,检查是否使用了实际值。如果您测试
It.IsAny
,请记住,您的代码中处理该值的部分可以替换为随机数生成器,单元测试仍然可以通过


话虽如此,Moq有一个限制,即如果列表中的一个参数未知,必须使用
它。IsAny
那么它们都必须使用(我不再使用它了,所以我不知道是否仍然如此,但我似乎记得您可以通过使用回调手动验证参数来解决这个问题)要回答这个问题

如何设置调试,以便在测试代码下逐步完成系统

在Visual Studio中,调试从
Start
按钮运行的系统时,无需执行任何特殊的设置

单元测试实际上只是强调
类的
公共
API的代码。单元测试和对公共API的实际调用之间的唯一区别在于,您有一些
属性
要添加到方法中,并且类中存在
mock
依赖项。 该类本身与正常调试时保持不变,但您为它设置了处理不同逻辑的场景,然后您可以
验证
是否正确发生

要让您的测试运行程序在
debug
中运行测试,您必须选择它在
debug
中运行,否则大多数运行程序默认在基本
release
模式下运行。根据您使用的运行程序的不同,这是以不同的方式完成的,但通常允许您右键单击
测试
调试
,并按照
Debu行选择一些内容
const string serial = "123456";
mockDeviceInteractions.Setup(m => m.GetDeviceSettingsForSerialNumber(string serial))
                      .Returns(new DeviceSettings { Serial = serial };

var settings = classUnderTest.GetDeviceSettingsForSerialNumber(serial);
settings.Serial.Should.Be(serial);
mockDeviceInteractions.Setup(x => x.GetDeviceSettings(It.IsAny<string>(), It.IsAny<string>()));
mockDeviceInteractions.Object.GetDeviceSettings("123123", "dfgdfg");
mockDeviceInteractions.SetupSet(p => p._settingsFileQueue = It.IsAny<string>());
mock.SetupProperty(f => f.Name, "foo");
[Test]
public void GetDeviceSettings_is_called()
{
    //Arrange
    var mockDeviceInteractions = new Mock<IDeviceInteractions>();
    var deviceSettings = new Mock<IDeviceSettings>();
    mockDeviceInteractions.Setup(x => x.GetDeviceSettings(@"123123", "dfgdfg"))
        .Returns(deviceSettings.Object)
        .Verifiable();

    //Act
    var actual = mockDeviceInteractions.Object.GetDeviceSettingsForSerialNumber(@"123123");

    //Assert
    Assert.Equal(deviceSettings.Object, actual);
    mockDeviceInteractions.Verify();
}