C# 单元测试使用模拟的void方法?

C# 单元测试使用模拟的void方法?,c#,unit-testing,moq,xunit,C#,Unit Testing,Moq,Xunit,我想用Mock测试void方法 public class ConsoleTargetBuilder : ITargetBuilder { private const string CONSOLE_WITH_STACK_TRACE = "consoleWithStackTrace"; private const string CONSOLE_WITHOUT_STACK_TRACE = "consoleWithoutStackTrace"; private LoggerMo

我想用Mock测试void方法

 public class ConsoleTargetBuilder : ITargetBuilder
{
    private const string CONSOLE_WITH_STACK_TRACE = "consoleWithStackTrace";
    private const string CONSOLE_WITHOUT_STACK_TRACE = "consoleWithoutStackTrace";
    private LoggerModel _loggerModel;
    private LoggingConfiguration _nLogLoggingConfiguration;

    public ConsoleTargetBuilder(LoggerModel loggerModel, LoggingConfiguration nLogLoggingConfiguration)
    {
        _loggerModel = loggerModel;
        _nLogLoggingConfiguration = nLogLoggingConfiguration;
    }

    public void AddNLogConfigurationTypeTagret()
    {
        var consoleTargetWithStackTrace = new ConsoleTarget();
        consoleTargetWithStackTrace.Name = CONSOLE_WITH_STACK_TRACE;
        consoleTargetWithStackTrace.Layout = _loggerModel.layout + "|${stacktrace}";
        _nLogLoggingConfiguration.AddTarget(CONSOLE_WITH_STACK_TRACE, consoleTargetWithStackTrace);

        var consoleTargetWithoutStackTrace = new ConsoleTarget();
        consoleTargetWithoutStackTrace.Name = CONSOLE_WITHOUT_STACK_TRACE;
        consoleTargetWithoutStackTrace.Layout = _loggerModel.layout;
        _nLogLoggingConfiguration.AddTarget(CONSOLE_WITHOUT_STACK_TRACE, consoleTargetWithoutStackTrace);
    }
问题是我不知道如何测试它。我有我的主代码

public class ConsoleTargetBuilderUnitTests
{
    public static IEnumerable<object[]> ConsoleTargetBuilderTestData
    {
        get
        {
            return new[]
            {
                 new object[]
                {
                    new LoggerModel(),
                    new LoggingConfiguration(),
                    2
                }
            };
        }
    }
    [Theory]
    [MemberData("ConsoleTargetBuilderTestData")]
    public void ConsoleTargetBuilder_Should_Add_A_Console_Target(LoggerModel loggerModel, LoggingConfiguration nLogLoggingConfiguration, int expectedConsoleTargetCount)
    {
        // ARRANGE
        var targetBuilderMock = new Mock<ITargetBuilder>();
        targetBuilderMock.Setup(x => x.AddNLogConfigurationTypeTagret()).Verifiable();
        // ACT
        var consoleTargetBuilder = new ConsoleTargetBuilder(loggerModel, nLogLoggingConfiguration);
        // ASSERT
        Assert.Equal(expectedConsoleTargetCount, nLogLoggingConfiguration.AllTargets.Count);
    }
公共类控制台GetBuilderUnitTests
{
公共静态IEnumerable控制台GetBuilderTestData
{
收到
{
返回新的[]
{
新对象[]
{
新的LoggerModel(),
新的LoggingConfiguration(),
2.
}
};
}
}
[理论]
[MemberData(“ConsoleTargetBuilderTestData”)]
public void ConsoleTargetBuilder_应该_添加_Console_目标(LoggerModel LoggerModel,LoggingConfiguration nLogLoggingConfiguration,int expectedConsoleTargetCount)
{
//安排
var targetBuilderMock=new Mock();
targetBuilderMock.Setup(x=>x.AddNLOGConfiguration类型Tagret()).Verifiable();
//表演
var consoleTargetBuilder=新的consoleTargetBuilder(loggerModel,nLogLoggingConfiguration);
//断言
相等(expectedConsoleTargetCount、nLogLoggingConfiguration.AllTargets.Count);
}

请引导我到正确的方向。

在我看来,您根本不需要模拟。您正在测试AddNLogConfiguration TypeTagret,而不是构造函数

// Assert
targetBuilderMock.Verify(x => x.AddNLogConfigurationTypeTagret(), Times.Once());
[Theory]
[MemberData("ConsoleTargetBuilderTestData")]
public void ConsoleTargetBuilder_Should_Add_A_Console_Target(LoggerModel loggerModel, LoggingConfiguration nLogLoggingConfiguration, int expectedConsoleTargetCount)
{
    // ARRANGE
    var consoleTargetBuilder = new ConsoleTargetBuilder(loggerModel, nLogLoggingConfiguration);
    // ACT
    consoleTargetBuilder.AddNLogConfigurationTypeTagret();
    // ASSERT
    Assert.Equal(expectedConsoleTargetCount, nLogLoggingConfiguration.AllTargets.Count);
}
如果您不想自己调用AddNLogConfiguration TypeTagret,则应在构造函数中调用它。这会将测试更改为:

[Theory]
[MemberData("ConsoleTargetBuilderTestData")]
public void ConsoleTargetBuilder_Should_Add_A_Console_Target(LoggerModel loggerModel, LoggingConfiguration nLogLoggingConfiguration, int expectedConsoleTargetCount)
{
    // ARRANGE
    // ACT
    var consoleTargetBuilder = new ConsoleTargetBuilder(loggerModel, nLogLoggingConfiguration);
    // ASSERT
    Assert.Equal(expectedConsoleTargetCount, nLogLoggingConfiguration.AllTargets.Count);
}
然后类构造函数应该是:

public ConsoleTargetBuilder(LoggerModel loggerModel, LoggingConfiguration nLogLoggingConfiguration)
{
    _loggerModel = loggerModel;
    _nLogLoggingConfiguration = nLogLoggingConfiguration;
    AddNLogConfigurationTypeTagret();
}

在我看来,您根本不需要模拟。您正在测试AddNLogConfiguration TypeTagret,而不是构造函数

[Theory]
[MemberData("ConsoleTargetBuilderTestData")]
public void ConsoleTargetBuilder_Should_Add_A_Console_Target(LoggerModel loggerModel, LoggingConfiguration nLogLoggingConfiguration, int expectedConsoleTargetCount)
{
    // ARRANGE
    var consoleTargetBuilder = new ConsoleTargetBuilder(loggerModel, nLogLoggingConfiguration);
    // ACT
    consoleTargetBuilder.AddNLogConfigurationTypeTagret();
    // ASSERT
    Assert.Equal(expectedConsoleTargetCount, nLogLoggingConfiguration.AllTargets.Count);
}
如果您不想自己调用AddNLogConfiguration TypeTagret,则应在构造函数中调用它。这会将测试更改为:

[Theory]
[MemberData("ConsoleTargetBuilderTestData")]
public void ConsoleTargetBuilder_Should_Add_A_Console_Target(LoggerModel loggerModel, LoggingConfiguration nLogLoggingConfiguration, int expectedConsoleTargetCount)
{
    // ARRANGE
    // ACT
    var consoleTargetBuilder = new ConsoleTargetBuilder(loggerModel, nLogLoggingConfiguration);
    // ASSERT
    Assert.Equal(expectedConsoleTargetCount, nLogLoggingConfiguration.AllTargets.Count);
}
然后类构造函数应该是:

public ConsoleTargetBuilder(LoggerModel loggerModel, LoggingConfiguration nLogLoggingConfiguration)
{
    _loggerModel = loggerModel;
    _nLogLoggingConfiguration = nLogLoggingConfiguration;
    AddNLogConfigurationTypeTagret();
}

一般来说,你不应该模拟你想要测试的类,你应该模拟它的依赖关系。当你开始模拟这个类时,你试图测试的东西往往会交织在一起,导致脆弱的测试,很难确定你是在测试你的代码,还是在模拟设置,或者两者兼而有之


方法
addnlogConfiguration TypeTagret
是您正在测试的类上的一个公共方法,因此@Philip说过,您可能应该从测试中调用它,或者应该从构造函数中调用它。如果您的目标是从构造函数中调用它,那么我建议它可能应该是一个私有方法d,除非有理由从课堂外调用它


就测试void方法调用的效果而言,您感兴趣的是该方法引起的状态更改。在本例中,
\u nLoggingConfiguration
是否有两个添加了正确属性的目标。您不会向我们显示
nLoggingConfiguration.AllTargets
属性,但我会从f假设您在测试中使用了
Count
属性,您只需检查列表中的项目,确认已将正确的名称和布局添加到正确的目标类型。

一般来说,您不应该模拟要测试的类,而应该模拟它的依赖项。当您启动mocki对您试图测试的类进行模拟通常会导致脆弱的测试,很难确定您是在测试代码,还是在模拟设置,或者两者兼而有之


方法
addnlogConfiguration TypeTagret
是您正在测试的类上的一个公共方法,因此@Philip说过,您可能应该从测试中调用它,或者应该从构造函数中调用它。如果您的目标是从构造函数中调用它,那么我建议它可能应该是一个私有方法d,除非有理由从课堂外调用它

就测试void方法调用的效果而言,您感兴趣的是该方法引起的状态更改。在本例中,
\u nLoggingConfiguration
是否有两个添加了正确属性的目标。您不会向我们显示
nLoggingConfiguration.AllTargets
属性,但我会从f假设您在测试中使用了
Count
属性,您可以简单地检查列表中的项目,以确认已将正确的名称和布局添加到正确的目标类型中

请引导我到正确的方向,我想要两件事。A)调用了该方法。B)添加了两个目标。因此预期计数为2


A)如果要验证调用了方法
AddNLogConfigurationTypeTagret
,则需要在调用此方法的代码上对其进行测试。例如,类
ConsoleTargetBuilderClient
是使用
ITargetBuilder
的类的假设示例(在您自己的代码中,应该有使用类
ConsoleTargetBuilder
的地方)


然后,您可以进行一个测试,验证名为方法
AddNLogConfiguration TypeTagret
的方法
DoSomething
是否正确。为此,您可以使用
ConsoleTargetBuilder
的模拟,因为您可以测试与该方法的交互。测试可能如下所示:

    public void DoSomething_WhenCalled_AddNLogConfigurationTypeTagretGetsCalled()
    {
        // Arrange
        bool addNLogConfigurationTypeTagretWasCalled = false;
        Mock<ITargetBuilder> targetBuilderMock = new Mock<ITargetBuilder>();
        targetBuilderMock.Setup(b => b.AddNLogConfigurationTypeTagret())
            .Callback(() => addNLogConfigurationTypeTagretWasCalled = true);
        ConsoleTargetBuilderClient client = new ConsoleTargetBuilderClient(targetBuilderMock.Object);

        // Act
        client.DoSomething();

        // Assert
        Assert.IsTrue(addNLogConfigurationTypeTagretWasCalled);
    }
public void AddNLogConfigurationTypeTagret_WhenCalled_ConsoleTargetsAdded()
{
    // Arrange
    const int expectedConsoleTargetCount = 2;
    var loggerModel = new LoggerModel();
    var nLogLoggingConfiguration = new LoggingConfiguration();
    var consoleTargetBuilder = new ConsoleTargetBuilder(loggerModel, nLogLoggingConfiguration);

    // Act
    consoleTargetBuilder.AddNLogConfigurationTypeTagret();

    // Assert
    Assert.AreEqual<int>(expectedConsoleTargetCount, nLogLoggingConfiguration.AllTargets.Count);
}
public void DoSomething\u when-called\u addnlogconfigurationtypetagretgetscaled()
{
//安排
bool addNLogConfigurationTypeTagretWasCalled=false;
Mock targetBuilderMock=new Mock();
targetBuilderMock.Setup(b=>b.addnlogConfiguration类型tagret())
.Callback(()=>AddNLOGConfiguration类型TagretwasCalled=true);
ConsoleGetBuilderClient=新的ConsoleGetBuilderClient(targetBuilderMock.Object);
//表演
client.DoSomething();
//断言
IsTrue(AddNLogConfiguration类型TagretWasCalled);
}
B)如果您愿意