简体   繁体   English

将模拟方法的结果返回到另一个模拟方法

[英]Return the result of a mocked method to another mocked method

I've got a class that has the following implementation: 我有一个具有以下实现的类:

public sealed class HotelRepository : IHotelRepository
{
    private readonly string _dataSource;

    public HotelRepository(string dataSource) => _dataSource = dataSource;

    /// <inheritdoc />
    public async Task<IEnumerable<Hotel>> GetAllAsync() =>
        await Task.Run(() => JObject.Parse(File.ReadAllText(_dataSource))["hotels"].ToList().Select(x => x.ToObject<Hotel>()));

    /// <inheritdoc />
    public async Task<IEnumerable<Hotel>> GetListByMatchAsync(string name) =>
        await GetAllAsync().ContinueWith(x => x.Result.Where(y => y.Name.Contains(name, StringComparison.CurrentCultureIgnoreCase)));
}

As you can see, the GetListByMatchAsync method calls GetAllAsync , then does some logic before returning the result. 如您所见, GetListByMatchAsync方法调用GetAllAsync ,然后在返回结果之前执行一些逻辑。

When I tried to mock this repository for unit testing, I'm struggling to get a result out of GetListByMatchAsync as it always fails as a null reference exception. 当我尝试模拟此存储库以进行单元测试时,我正在努力从GetListByMatchAsync获取结果,因为它始终会作为空引用异常而失败。

Here's the unit test: 这是单元测试:

[TestCase("Test", "X")]
[TestCase("Hotel", "X")]
[TestCase("Name", "X")]
public async Task GetListByMatchAsync_GetHotelListByMatchingNameAsync_ReturnsFiveMatchingHotels(string name, string nonMatch)
{

    _hotelRepositoryMock = new Mock<IHotelRepository>();
    _hotelRepository = _hotelRepositoryMock.Object;

    // Set up sample data.
    var data = new List<Hotel>
    {
        new Hotel{Id = 1, Name = $"{name}", Description = "Description2", Location = "Location2", Rating = Rating.Two},
        new Hotel{Id = 2, Name = $"{name.ToUpper()}", Description = "Description1", Location = "Location1", Rating = Rating.Five},
        new Hotel{Id = 3, Name = $"{name.ToLower()}", Description = "Description2", Location = "Location2", Rating = Rating.Three},
        new Hotel{Id = 4, Name = $"{name} {nonMatch}", Description = "Description2", Location = "Location2", Rating = Rating.One},
        new Hotel{Id = 5, Name = nonMatch, Description = "Description2", Location = "Location2", Rating = Rating.One},
    };

    // Set up mock methods and ensure these method returns any sample data.
    _hotelRepositoryMock.Setup(x => x.GetListByMatchAsync(It.IsAny<string>()));
    _hotelRepositoryMock.Setup(x => x.GetAllAsync()).ReturnsAsync(data);


    var result = await _hotelRepository.GetListByMatchAsync(name);

    // Cast to list to make assertions.
    var hotels = result.ToList();

    Assert.That(hotels, Is.TypeOf<List<Hotel>>());
    Assert.That(hotels.Count, Is.EqualTo(4));
}

How can I make this test work such that the GetListByMatchAsync mock method does some logic after calling the mocked GetAllAsync method? 我怎样才能让这个测试工作,使得GetListByMatchAsync模拟方法做了一些逻辑调用嘲笑后GetAllAsync方法?

First, the code that you have shown cannot possibly work because the methods that you are trying to set up are not virtual. 首先,您显示的代码可能无法正常工作,因为您尝试设置的方法不是虚拟的。 Declare the methods you want to Setup as virtual . 声明要Setupvirtual

Second, this is wrong: 其次,这是错误的:

 _hotelRepositoryMock.Setup(x => x.GetListByMatchAsync(It.IsAny<string>())); 

With this call, you're effectively setting up GetListByMatchAsync to return default(Task<IEnumerable<Hotel>>) , ie null . 通过此调用,您可以有效地设置GetListByMatchAsync以返回default(Task<IEnumerable<Hotel>>) ,即null That's obviously not what you want. 那显然不是您想要的。 Either: 或者:

  • use .Returns(...) to specify what the method should return; 使用.Returns(...)指定方法应返回的内容; or, 要么,
  • use .CallBase() if the method should simply return whatever the implementation in the base class would return. 如果方法应该简单地返回基类中的实现将返回的任何内容,则使用.CallBase() (This is likely what you need.) (这可能是您所需要的。)

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

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