-2

I'm trying to test a method that takes a dictionary as a parameter, filters it by removing some unnecessary entries and returns the resulting dictionary. I have separately written the tests for the filtering class working correctly so this test is only concerned with the filterer being called and returning the dictionary.

For some reason, the filterer returns empty dictionary even though I specifically tell it what to return with FakeItEasy. The _callToChartOfAccountsFilterer assertion fails with the following message:

    Assertion failed for the following call:
    TinyBooks.Services.Json.ChartOfAccounts.IChartOfAccountsFilterer.FilterAccounts(dictionary: Ignored, filteringParameters: Ignored)
  Expected to find it once exactly but didn't find it among the calls:
    1: TinyBooks.Services.Json.ChartOfAccounts.IChartOfAccountsFilterer.FilterAccounts(dictionary: [[Group1, System.Collections.Generic.List`1[TinyBooks.DomainModel.Dtos.AccountForParserDto]]], filteringParameters: TinyBooks.Services.Json.ChartOfAccounts.ChartOfAccountsFilteringParameters)

ChartOfAccountsParser

public class ChartOfAccountsParser : IChartOfAccountsParser
{
    private readonly IJsonDeserializer _jsonDeserializer;
    private readonly IChartOfAccountsFilterer _chartOfAccountsFilterer;

    public ChartOfAccountsParser(IJsonDeserializer jsonDeserializer, IChartOfAccountsFilterer chartOfAccountsFilterer)
    {
        _jsonDeserializer = jsonDeserializer;
        _chartOfAccountsFilterer = chartOfAccountsFilterer;
    }

    public Dictionary<string, List<AccountForParserDto>> GetChartOfAccountsDictionaryBasedOnJsonFile(
        ChartOfAccountsParsingParameters parsingParameters)
    {
        var filePath = GetPathOfJsonFile(parsingParameters.FileName);
        var dictionary = ParseJsonToDictionary(filePath);
        var dic = _chartOfAccountsFilterer.FilterAccounts(dictionary, (ChartOfAccountsFilteringParameters)parsingParameters);
        return dic;
    }

    private string GetPathOfJsonFile(string fileName) => Path.Combine(GetFolderOfExecutingAssembly(), "ChartOfAccounts\\Json", fileName);

    private string GetFolderOfExecutingAssembly()
        => new Uri(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? throw new InvalidOperationException()).LocalPath;

    private Dictionary<string, List<AccountForParserDto>> ParseJsonToDictionary(string filePath) 
        => _jsonDeserializer.Load<Dictionary<string, List<AccountForParserDto>>>(filePath);
}

ChartOfAccountsParserTests

 [TestFixture]
public class ChartOfAccountsParserTests
{
    private IJsonDeserializer _jsonDeserializer;
    private IChartOfAccountsFilterer _chartOfAccountsFilterer;
    private AccountForParserDto _dto;
    private ChartOfAccountsParsingParameters _parameters;
    private string _key;
    private List<AccountForParserDto> _group;
    private Dictionary<string, List<AccountForParserDto>> _dictionary;
    private IReturnValueArgumentValidationConfiguration<Dictionary<string, List<AccountForParserDto>>> _callToJsonDeserializer;
    private IReturnValueArgumentValidationConfiguration<Dictionary<string, List<AccountForParserDto>>> _callToChartOfAccountsFilterer;

    [SetUp]
    public void Setup()
    {
        _jsonDeserializer = A.Fake<IJsonDeserializer>();
        _chartOfAccountsFilterer = A.Fake<IChartOfAccountsFilterer>();

        _dto = new AccountForParserDto { Name = "Account1" };
        _parameters = new ChartOfAccountsParsingParameters { FileName = "a.json" };
        _key = "Group1";
        _group = new List<AccountForParserDto> { _dto };
        _dictionary = new Dictionary<string, List<AccountForParserDto>> { [_key] = _group} ;

        _callToJsonDeserializer = A.CallTo(() => _jsonDeserializer.Load<Dictionary<string, List<AccountForParserDto>>>(A<string>._));
        _callToChartOfAccountsFilterer = A.CallTo(() => _chartOfAccountsFilterer.FilterAccounts(
            A<Dictionary<string, List<AccountForParserDto>>>._,
            A<ChartOfAccountsParsingParameters>._));

        _callToJsonDeserializer.Returns(_dictionary);
        _callToChartOfAccountsFilterer.Returns(_dictionary);
    }

    [Test]
    public void GetChartOfAccountsDictionaryBasedOnJsonFile_GivenDictionaryIgnoringParameters_ReturnsSameDictionary()
    {
        var parser = new ChartOfAccountsParser(_jsonDeserializer, _chartOfAccountsFilterer);

        var result = parser.GetChartOfAccountsDictionaryBasedOnJsonFile(_parameters);

        Assert.That(result.ContainsKey(_key));
        Assert.That(result.ContainsValue(_group));
        _callToJsonDeserializer.MustHaveHappenedOnceExactly();
        _callToChartOfAccountsFilterer.MustHaveHappenedOnceExactly();
    }
}
Karol Skrobot
  • 450
  • 3
  • 19
  • Your code doesn't compile. Even putting aside some missing classes, `ChartOfAccountsParser.GetChartOfAccountsDictionaryBasedOnJsonFile` makes use of the variable `dictionary` before it's even declared. I've no idea what the actual method does, so it's hard to tell what's going wrong for you. – Blair Conrad Nov 04 '19 at 23:36
  • 1
    However, when I replace the call with `var dictionary = _chartOfAccountsFilterer.FilterAccounts(new Dictionary>());`, the test passes. – Blair Conrad Nov 04 '19 at 23:42
  • @BlairConrad I removed some code for clarity but I went too far. I'll revert the edit – Karol Skrobot Nov 05 '19 at 20:39

2 Answers2

2

Well I figured it out finally.

The problem was with the type of the ignored parameter in this call:

_callToChartOfAccountsFilterer = A.CallTo(() => _chartOfAccountsFilterer.FilterAccounts(
            A<Dictionary<string, List<AccountForParserDto>>>._,
            A<ChartOfAccountsParsingParameters>._));

What I didn't show (unfortunately) was the signature of the method called:

public interface IChartOfAccountsFilterer
{
    Dictionary<string, List<AccountForParserDto>> FilterAccounts(
        Dictionary<string, List<AccountForParserDto>> dictionary,
        IChartOfAccountsFilteringParameters filteringParameters);
}

When I changed A<ChartOfAccountsParsingParameters>._ to A<IChartOfAccountsFilteringParameters>._ the test passed.

Karol Skrobot
  • 450
  • 3
  • 19
0

Using the following supporting members as a proof of concept based on your example.

public class Parser {
    private readonly IFilter filter;
    private readonly IProvider provider;

    public Parser(IFilter filter, IProvider provider) {
        this.filter = filter;
        this.provider = provider;
    }

    public Dictionary<string, List<Dto>> GetData() {
        Dictionary<string, List<Dto>> input = provider.Load();
        var dictionary = filter.Filter(input);
        return dictionary;
    }
}

public interface IProvider {
    Dictionary<string, List<Dto>> Load();
}

public interface IFilter {
    Dictionary<string, List<Dto>> Filter(Dictionary<string, List<Dto>> input);
}

public class Dto {
    public string Name { get; set; }
}

The following test behaves as expected

[TestClass]
public class DictionaryTests {
    [TestMethod]
    public void Sohuld_Return_Expected_Data() {
        //Arrange
        var key = "Group1";
        var dto = new Dto { Name = "Account1" };
        var group = new List<Dto> { dto };
        var dictionary = new Dictionary<string, List<Dto>> {
            { key, group }
        };

        var provider = A.Fake<IProvider>();
        A.CallTo(() => provider.Load()).Returns(dictionary);

        var filter = A.Fake<IFilter>();
        A.CallTo(() => filter.Filter(A<Dictionary<string, List<Dto>>>._))
            .Returns(dictionary);

        var subjectUnderTest = new Parser(filter, provider);

        //Act
        var actual = subjectUnderTest.GetData();

        //Assert - using FluentAssertions
        actual.ContainsKey(key).Should().BeTrue();
        actual.ContainsValue(group).Should().BeTrue();
    }
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472