单元测试和使用 Moq 中的问题。Returns

Issue in unit testing and using Moq .Returns

我是编写单元测试的新手。因此,我一直在苦苦挣扎。

我需要通过外部网络服务插入产品。然后 WebService 将 return 之后更新产品所必需的字符串。

这是我的 ApiController:

public class ProductController : ApiController
{
    private IProductRepository _ProductRepository;
    private IWebService _WebService;

    public ProductController(IProductRepository productRepository, IWebService webService)
    {
        _ProductRepository = productRepository;
        _WebService = webService;
    }

    public HttpResponseMessage Add(string title)
    {
        using (TransactionScope scope = new TransactionScope())
        {
            Product product = new Product
            {
                Title = title
            };

            this._ProductRepository.Add(product);

            // WebService will return a string
            string result = this._WebService.Add(product.ID, DateTime.Now); 

            product.ServiceResult = result;

            this._ProductRepository.Update(product);

            scope.Complete();
        }

        return Request.CreateResponse(HttpStatusCode.Created);
    }
}

我想知道我应该如何为这段代码编写单元测试?

我试过这样写:(使用 NUnit,Moq)

[TestFixture]
public class ProductControllerShould : AssertionHelper
{
    private Mock<IWebService> _WebService;
    private Mock<IProductRepository> _ProductRepository;

    [SetUp]
    public void Setup()
    {
        _WebService = new Mock<IWebService>();
        _ProductRepository = new Mock<IProductRepository>();
    }

    [Test]
    public void ReturnCreatedOnAdd()
    {
        _WebService.Setup(b => b.Add(1, DateTime.Now))
            .Returns("0");

        var controller = new ProductController(_ProductRepository.Object, 
                         _WebService.Object);

        var result = controller.Add("Lumia");

        Expect(result, Is.EqualTo(HttpStatusCode.Created));
    }
}

但是当我调试测试时,这一行的结果是空的,这是不正确的。

string result = this._WebService.Add(product.ID, DateTime.Now);

这一行不应该处理 _WebService.Add() 和 return“0”的行为吗?

_WebService.Setup(b => b.Add(1, DateTime.Now))
                .Returns("0");

我知道我写错了测试,但我不知道该怎么办。

谢谢。

这里的问题是你在模拟调用静态方法 `DateTime.Now' 。但是"Now"在mocking的时间点和as it called是不一样的。因此你的电话没有 return 任何东西。

我可以建议以下 3 种方式:

1) 调用是否使用 DateTime.Now 对你来说并不重要,在这种情况下你可以忽略第二个参数:

_WebService.Setup(b => b.Add(1, It.IsAny<DateTime>())).Returns("0");

2) 您想测试呼叫是与 DateTime.Now 进行的。在那种情况下,我创建了一个 interface 来获取 DateTime.Now:

public interface IDateTimeNowProvider
{
    DateTime Now { get; }
}

public ProductController(IProductRepository productRepository,
                     IWebService webService, 
                     IDateTimeNowProvider dateTimeNowProvider)
{
    _ProductRepository = productRepository;
    _WebService = webService;
    _dateTimeNowProvider = dateTimeNowProvider;
}

在生产代码中,您使用它的默认实现,即 returns DateTime.Now。但是在你的测试中 class 你用一些预定义的值来模拟这个接口并且你测试使用这个值。

var now = DateTime.Parse("2017-01-22");
var _dateTimeNowProvider = new Mock<IDateTimeNowProvider>();
var controller = new ProductController(_ProductRepository.Object, 
                        _WebService.Object, _dateTimeNowProvider.Object );
_dateTimeNowProvider.Setup(x => x.Now).Returns(now);
_WebService.Setup(b => b.Add(1,now)).Returns("0");

3) 您可以使用允许模拟静态方法的特殊模拟框架,例如 typemock isolator