繁体   English   中英

模拟没有任何服务参考C#的控制器进行单元测试

[英]Mock a controller for unit test without any service reference C#

我有一个简单的控制器,需要进行单元测试而不是集成测试。 我只需要一种模拟方法,以便可以验证是否调用了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);
    }
}

为此,您需要将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();
    }
}

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);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM