tjugg
tjugg

Reputation: 3357

Using moq to setup a method to return a list of objects but getting null

I've been testing around in my personal projects and ran to this little problem. I have a test method that creates a list of objects, I setup a service I use in my method I test to return the mock list. How ever, for some reason the setup is not working and it is returning null.

Here is the test method:

var mockList = new List<IBillItem>
{
    new BillItem
    {
        Id = 0,
        DueDate = new DateTime(),
        Name = "",
        IndexNumber = "",
        AccountNumber = "",
        Amount = decimal.One
    },
    new BillItem
    {
        Id = 0,
        DueDate = new DateTime(),
        Name = "",
        IndexNumber = "",
        AccountNumber = "",
        Amount = decimal.One
    }
};

_billHandlingService.Setup(x => x.GetAllBillsAsync(It.IsAny<string>())).Returns(Task.FromResult(mockList));

var listBillsVm = new ListBillsViewModel(new LoggerFactory(), _billHandlingService.Object, _settingsService.Object);

await listBillsVm.GetBillsAsync();

_billHandlingService.Verify(x => x.GetAllBillsAsync(_settingsService.Name), Times.AtMostOnce);

Assert.AreEqual(1, listBillsVm.BillsList.Count);

And here is the code of the concrete class im testing:

public async Task GetBillsAsync()
{
    BillsList.Clear();

    var bills = await _billHandlingService.GetAllBillsAsync(_settingsService.LoggerUser);

    if (null != bills)
    {
        var billsByDate = bills.Where(x => x.DueDate == DateTime.Today).ToList();
        foreach (var bill in billsByDate)
        {
            BillsList.Add(bill);
            RaisePropertyChanged(nameof(BillsList));
        }
    }
}

I Have tried searching SO / google for results an yet to find any answer. Thanks in advance.

Edit: The code is not commented but I believe its clear enough, ask in comments if there's something that needs clearing

Edit 2:

Task<List<IBillItem>>GetAllBillsAsync(string username); 

Is the interface for the method being called.

Upvotes: 8

Views: 10257

Answers (2)

Nkosi
Nkosi

Reputation: 247323

You could Try changing the .Returns in the Setup to .ReturnsAsync(mockList)

//...other code removed for brevity
_billHandlingService
    .Setup(x => x.GetAllBillsAsync(It.IsAny<string>()))
    .ReturnsAsync(mockList);
//...other code removed for brevity

UPDATE

The Following minimal complete verifiable example was used based on your question to try and reproduce your issue. Note that I omitted any classes that were not necessary to create the test.

class ListBillsViewModel {
    private IBillHandlingService _billHandlingService;
    private ISettingsService _settingsService;

    public ListBillsViewModel(IBillHandlingService billHandlingService, ISettingsService settingsService) {
        this._billHandlingService = billHandlingService;
        this._settingsService = settingsService;
        BillsList = new List<IBillItem>();
    }

    public List<IBillItem> BillsList { get; set; }

    public async Task GetBillsAsync() {
        BillsList.Clear();

        var bills = await _billHandlingService.GetAllBillsAsync(_settingsService.LoggerUserName);

        if (null != bills) {
            var billsByDate = bills.Where(x => x.DueDate == DateTime.Today).ToList();
            foreach (var bill in billsByDate) {
                BillsList.Add(bill);
            }
        }
    }
}

public interface ISettingsService {
    string Name { get; }
    string LoggerUserName { get; set; }
}

public interface IBillHandlingService {
    Task<List<IBillItem>> GetAllBillsAsync(string username);
}

public class BillItem : IBillItem {
    public int Id { get; set; }
    public DateTime DueDate { get; set; }
    public string Name { get; set; }
    public string IndexNumber { get; set; }
    public string AccountNumber { get; set; }
    public decimal Amount { get; set; }
}

public interface IBillItem {
    int Id { get; set; }
    DateTime DueDate { get; set; }
    string Name { get; set; }
    string IndexNumber { get; set; }
    string AccountNumber { get; set; }
    decimal Amount { get; set; }
}

The following Unit test was then reconstructed based on the above classes

[TestMethod]
public async Task Moq_Setup_Should_Return_List_Of_Objects() {
    var mockList = new List<IBillItem>
    {
        new BillItem
        {
            Id = 0,
            DueDate = DateTime.Today,
            Name = "User",
            IndexNumber = "",
            AccountNumber = "",
            Amount = decimal.One
        },
        new BillItem
        {
            Id = 1,
            DueDate = DateTime.Today.AddDays(1),
            Name = "User",
            IndexNumber = "",
            AccountNumber = "",
            Amount = decimal.One
        }
    };

    string name = "User";

    var _settingsService = new Mock<ISettingsService>();
    _settingsService
        .Setup(m => m.Name)
        .Returns(name);
    _settingsService
        .Setup(m => m.LoggerUserName)
        .Returns(name);

    var _billHandlingService = new Mock<IBillHandlingService>();
    _billHandlingService
        .Setup(x => x.GetAllBillsAsync(It.IsAny<string>()))
        .ReturnsAsync(mockList);

    var listBillsVm = new ListBillsViewModel(_billHandlingService.Object, _settingsService.Object);

    await listBillsVm.GetBillsAsync();

    _billHandlingService.Verify(x => x.GetAllBillsAsync(_settingsService.Name), Times.AtMostOnce);

    Assert.AreEqual(1, listBillsVm.BillsList.Count);
}

I ran the above test and it passes as expected for both setups with .Returns(Task.FromResult(mockist)) and .ReturnsAsync(mockList).

Either the example you gave does not match your actual situation or the problem is outside of what you are describing in your post.

Upvotes: 8

David Pine
David Pine

Reputation: 24535

You need to specify the List<IBillItem> on the Task.FromResult, like this:

_billHandlingService.Setup<Task<List<IBillItem>>>(
    x => x.GetAllBillsAsync(It.IsAny<string>()))
                    .Returns(Task.FromResult<List<IBillItem>>(mockList));

Similar SO Q and A here.

Upvotes: 0

Related Questions