模拟控制器进行单元测试,无需任何服务参考C#

时间:2017-11-21 15:14:30

标签: c# unit-testing moq

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

2 个答案:

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