我有一个简单的控制器,需要进行单元测试而不是集成测试。我只需要一种模拟方法,以便我可以验证是否调用了receive方法。我们已经对Receive()进行了测试,因此无需验证该方法内部的内容。
我的代码看起来像
public class MessageController : Controller
{
private readonly ConnectionDetail connectionDetail;
private readonly QueueDetail queueDetail;
public MessageController(IOptions<ConnectionDetail> connectionDetail, IOptions<QueueDetail> queueDetail)
{
this.connectionDetail = connectionDetail.Value;
this.queueDetail = queueDetail.Value;
}
[HttpGet()]
public IActionResult Get()
{
try
{
var channel = CreateConnectionAndChannel(queueDetail);
var message = channel.Receive();
var hbaseKey = new HbaseKey { Key = new Guid(message) };
return Ok(hbaseKey);
}
catch
{
return StatusCode(500, "Exception occured while processing. Try again.");
}
}
private IChannel CreateConnectionAndChannel(QueueDetail queueDetail)
{
var factory = new Factory();
var adapter = factory.Connect(MessagingType.MQ, connectionDetail);
return adapter.BindQueue(queueDetail);
}
}
答案 0 :(得分:1)
为此,您需要将CreateConnectionAndChannel方法移动到单独的依赖项,例如,实现IChannelFactory接口的ChannelFactory。
public interface IChannelFactory {
IChannel CreateConnectionAndChannel(QueueDetail queueDetail);
}
public class ChannelFactory : IChannelFactory {
public IChannel CreateConnectionAndChannel(QueueDetail queueDetail)
{
var factory = new Factory();
var adapter = factory.Connect(MessagingType.MQ, connectionDetail);
return adapter.BindQueue(queueDetail);
}
}
public class MessageController : Controller
{
private readonly ConnectionDetail connectionDetail;
private readonly QueueDetail queueDetail;
private readonly IChannelFactory channelFactory;
public MessageController(IOptions<ConnectionDetail> connectionDetail, IOptions<QueueDetail> queueDetail, IChannelFactory channelFactory)
{
this.connectionDetail = connectionDetail.Value;
this.queueDetail = queueDetail.Value;
this.channelFactory = channelFactory;
}
[HttpGet()]
public IActionResult Get()
{
try
{
var channel = channelFactory.CreateConnectionAndChannel(queueDetail);
var message = channel.Receive();
var hbaseKey = new HbaseKey { Key = new Guid(message) };
return Ok(hbaseKey);
}
catch
{
return StatusCode(500, "Exception occured while processing. Try again.");
}
}
}
之后你可以在测试中模拟你的控制器(例如使用Moq):
[TestFixture]
public class TestMessageController
{
[Test]
public void TestGet()
{
var channelMock = new Mock<IChannel>(MockBehavior.Strict);
channelMock
.Setup(c => c.Receive())
.Returns(null);
var channelFactoryMock = new Mock<IChannelFactory>(MockBehavior.Strict);
channelFactory
.Setup(cf => cf.CreateConnectionAndChannel(It.IsAny<IOptions<QueueDetail>>()))
.Returns();
var controller = new MessageController(null, null, channelFactoryMock.Object);
controller.Get();
}
}
答案 1 :(得分:1)
将CreateConnectionAndChannel
功能重构为自己的服务
public interface IChannelProvider {
IChannel CreateConnectionAndChannel();
}
并让控制器明确依赖该服务
public class MessageController : Controller {
private readonly IChannelProvider channelProvider;
public MessageController(IChannelProvider channelProvider) {
this.channelProvider = channelProvider;
}
[HttpGet()]
public IActionResult Get() {
try {
var channel = channelProvider.CreateConnectionAndChannel();
var message = channel.Receive();
var hbaseKey = new HbaseKey { Key = new Guid(message) };
return Ok(hbaseKey);
} catch {
return StatusCode(500, "Exception occured while processing. Try again.");
}
}
}
所以现在只需要模拟IChannelProvider
来单独测试控制器。
我只需要一种模拟方法,以便我可以验证是否调用了receive方法。
public void Verify_Received_Called() {
//Arrange
var channel = new Mock<IChannel>();
channel
.Setup(_ => _.Receive())
.Returns("My mock value here");
var mockProvider = new Mock<IChannelProvider>();
mockProvider.Setup(_ => _.CreateConnectionAndChannel())
.Returns(channel.Object);
var controller = new MessageController(mockProvider.Object);
//Act
var result = controller.Get();
//Assert
channel.Verify(_ => _.Receive(), Times.AtLeastOnce);
}
提供者实现可能看起来像......
public class ChannelProvider : IChannelProvider {
private readonly ConnectionDetail connectionDetail;
private readonly QueueDetail queueDetail;
public ChannelProvider(IOptions<ConnectionDetail> connectionDetail, IOptions<QueueDetail> queueDetail) {
this.connectionDetail = connectionDetail.Value;
this.queueDetail = queueDetail.Value;
}
public IChannel CreateConnectionAndChannel() {
var factory = new Factory();
var adapter = factory.Connect(MessagingType.MQ, connectionDetail);
return adapter.BindQueue(queueDetail);
}
}