如何覆盖方法 DoMethod() 的构造函数参数空异常和 catch 块
how to cover both constructor argument null exception and catch block for method `DoMethod()`
我有以下单元测试 C# 代码,
public class ServiceTest
{
public readonly Service _sut;
private readonly Mock<IServiceClient> _serviceClientMock = new Mock<IServiceClient>();
private readonly Mock<ILogger<Service>> _loggerMock = new Mock<ILogger<Service>>();
public ServiceTest()
{
_sut = new Service(_serviceClientMock.Object, _loggerMock.Object);
}
[Fact]
public void Constructor_throws_Exception()
{
Assert.Throws<ArgumentNullException>(() => new Service(null, null));
}
[Fact]
public async Task Do_Test_For_DoMethod()
{
await _sut.DoMethod();
}
}
我有 Constructor_throws_Exception
,它只涵盖一个参数 null 异常,但不涵盖另一个。如何同时覆盖参数空异常和方法的 catch 块?有没有一种方法可以在一次测试中合并所有内容?我正在使用 xUnit
.
您必须为每个无效组合创建一个唯一的测试。可能是这样的:
public static IEnumerable<object[]> GetInvalidConstructorArguments()
{
yield return new object[] { new Mock<IServiceClient>().Object, null };
yield return new object[] { null, new Mock<ILogger<Service>>().Object };
}
[Theory]
[MemberData(nameof(GetInvalidConstructorArguments))]
public void ThrowsOnNullArgument(IServiceClient serviceClient, ILogger<Service> logger)
{
Assert.Throws<ArgumentNullException>(() => new Service(serviceClient, logger));
}
为 ILogger<>
获取工作模拟比在第一个位置看起来要复杂得多。问题是,所有方便的方法都是扩展方法,不能被模拟。在幕后,所有这些方法都将调用必须模拟的 Log<TState>()
方法。感谢 ,这可以按如下方式完成:
public class MyTests
{
[Fact]
public void ExceptionShouldBeWrittenToLog()
{
// Instruct service client to throw exception when being called.
var serviceClient = new Mock<IServiceClient>();
var exception = new InvalidOperationException($"Some message {Guid.NewGuid()}");
serviceClient.Setup(s => s.Do()).Throws(exception);
// Create a strict mock, that shows, if an error log should be created.
var logger = new Mock<ILogger<MyService>>(MockBehavior.Strict);
logger.Setup(l => l.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((o, t) => o.ToString() == exception.Message),
It.IsAny<InvalidOperationException>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()));
// Setup SUT and call method.
var service = new MyService(serviceClient.Object, logger.Object);
service.DoSomething();
// Check if method of logger was being called.
logger.VerifyAll();
}
}
public interface IServiceClient
{
public void Do();
}
public class MyService
{
private readonly IServiceClient serviceClient;
private readonly ILogger<MyService> logger;
public MyService(IServiceClient serviceClient, ILogger<MyService> logger)
{
this.serviceClient = serviceClient;
this.logger = logger;
}
public void DoSomething()
{
try
{
serviceClient.Do();
}
catch (Exception ex)
{
logger.LogError(ex.Message);
}
}
}
我有以下单元测试 C# 代码,
public class ServiceTest
{
public readonly Service _sut;
private readonly Mock<IServiceClient> _serviceClientMock = new Mock<IServiceClient>();
private readonly Mock<ILogger<Service>> _loggerMock = new Mock<ILogger<Service>>();
public ServiceTest()
{
_sut = new Service(_serviceClientMock.Object, _loggerMock.Object);
}
[Fact]
public void Constructor_throws_Exception()
{
Assert.Throws<ArgumentNullException>(() => new Service(null, null));
}
[Fact]
public async Task Do_Test_For_DoMethod()
{
await _sut.DoMethod();
}
}
我有 Constructor_throws_Exception
,它只涵盖一个参数 null 异常,但不涵盖另一个。如何同时覆盖参数空异常和方法的 catch 块?有没有一种方法可以在一次测试中合并所有内容?我正在使用 xUnit
.
您必须为每个无效组合创建一个唯一的测试。可能是这样的:
public static IEnumerable<object[]> GetInvalidConstructorArguments()
{
yield return new object[] { new Mock<IServiceClient>().Object, null };
yield return new object[] { null, new Mock<ILogger<Service>>().Object };
}
[Theory]
[MemberData(nameof(GetInvalidConstructorArguments))]
public void ThrowsOnNullArgument(IServiceClient serviceClient, ILogger<Service> logger)
{
Assert.Throws<ArgumentNullException>(() => new Service(serviceClient, logger));
}
为 ILogger<>
获取工作模拟比在第一个位置看起来要复杂得多。问题是,所有方便的方法都是扩展方法,不能被模拟。在幕后,所有这些方法都将调用必须模拟的 Log<TState>()
方法。感谢
public class MyTests
{
[Fact]
public void ExceptionShouldBeWrittenToLog()
{
// Instruct service client to throw exception when being called.
var serviceClient = new Mock<IServiceClient>();
var exception = new InvalidOperationException($"Some message {Guid.NewGuid()}");
serviceClient.Setup(s => s.Do()).Throws(exception);
// Create a strict mock, that shows, if an error log should be created.
var logger = new Mock<ILogger<MyService>>(MockBehavior.Strict);
logger.Setup(l => l.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((o, t) => o.ToString() == exception.Message),
It.IsAny<InvalidOperationException>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()));
// Setup SUT and call method.
var service = new MyService(serviceClient.Object, logger.Object);
service.DoSomething();
// Check if method of logger was being called.
logger.VerifyAll();
}
}
public interface IServiceClient
{
public void Do();
}
public class MyService
{
private readonly IServiceClient serviceClient;
private readonly ILogger<MyService> logger;
public MyService(IServiceClient serviceClient, ILogger<MyService> logger)
{
this.serviceClient = serviceClient;
this.logger = logger;
}
public void DoSomething()
{
try
{
serviceClient.Do();
}
catch (Exception ex)
{
logger.LogError(ex.Message);
}
}
}