无法在c# xunit中使用Auto Fixture创建自定义对象

问题描述 投票:0回答:2

我有下面的类和类别项映射的字典

public class InfoClass
    {
        public InfoClass()
        {
            this.InfoID = string.Empty;
            this.EDDetails = string.Empty;
            this.EVDetails = string.Empty;
        }
        public int ID { get; set;}
        public string InfoID { get; set;}
        public string EDDetails { get; set; }
        public string EVDetails { get; set; }
    }


    private static Dictionary<string, List<string>> ItemsMap = new Dictionary<string, List<string>>
            {
                { Constants.ITEMCATEGORY_ED, new List<string> { "XYZ", "IDASAS" } },
                { Constants.ITEMCATEGORY_ERANDRE, new List<string> { "SAS", "PQR" } }
            
            };

我想以这样的方式创建 InfoClass 模拟对象,InfoClass 的 InfoID 应该是从字典内的列表中随机生成的。根据所选 InfoID 的类别,为 EDDetails 或 EVDetials 分配一个值。

例如,如果 ReportId 的随机值为“XYZ”,则 InfoID 的类别为 ITEMCATEGORY_ED,则将 {根据逻辑生成数据} 值分配给 EDDetails 属性,所有其他属性 EVDetails 都应为空。

为了实现这一目标,我尝试使用下面的方法创建自定义。

public class InfoClassCustomization : ICustomization
    {
        private readonly Dictionary<string, List<string>> ItemsMap;

        public ArchivedOrderLineItemInfoCustomization(Dictionary<string, List<string>> ItemsMap)
        {
            this.ItemsMap = ItemsMap;
        }

        public virtual void Customize(IFixture fixture)
        {
            fixture.Customize<InfoClass>(c => c
                .Do(o =>
                {
                    // Flatten your dictionary to a list of tuples
                    var keyValueList = ItemsMap.SelectMany(kvp => kvp.Value.Select(v => (Key: kvp.Key, Value: v))).ToList();

                    // Pick a random tuple
                    var InfoIdPair = keyValueList[new Random().Next(keyValueList.Count)];

                    // Set the ReportID from the randomly selected tuple
                    o.ReportID = InfoIdPair.Value;


                    // Set the correct property based on the key of the randomly selected tuple
                    switch (InfoIdPair.Key)
                    {
                        case Constants.ITEMCATEGORY_ED:
                            o.EDDetails = JsonConvert.SerializeObject(fixture.Create<EdDetails>());
                            break;
                        case Constants.ITEMCATEGORY_ERANDRE:
                            o.EVDetails = JsonConvert.SerializeObject(fixture.Create<EvDetails>());
                            break;
                    }
                }));
        }
    }



var fixture = new Fixture().Customize(new InfoClassCustomization(ItemsMap));

var obj = fixture.Create<InfoClass>();

但这并没有奏效。连个例外都没有。所以尝试了下面

public static InfoClass GetItemInfo()
        {
            var fixture = new Fixture();
            InfoClass o = new InfoClass();
            // Flatten your dictionary to a list of tuples
                    var keyValueList = ItemsMap.SelectMany(kvp => kvp.Value.Select(v => (Key: kvp.Key, Value: v))).ToList();

                    // Pick a random tuple
                    var InfoIdPair = keyValueList[new Random().Next(keyValueList.Count)];

                    // Set the ReportID from the randomly selected tuple
                    o.ReportID = InfoIdPair.Value;


                    // Set the correct property based on the key of the randomly selected tuple
                    switch (InfoIdPair.Key)
                    {
                        case Constants.ITEMCATEGORY_ED:
                            o.EDDetails = JsonConvert.SerializeObject(fixture.Create<EdDetails>());
                            break;
                        case Constants.ITEMCATEGORY_ERANDRE:
                            o.EVDetails = JsonConvert.SerializeObject(fixture.Create<EvDetails>());
                            break;
                    }

            return o;
        }

var fixture = new Fixture();
            fixture.Customize<ArchivedOrderLineItemInfo>(c => c
            .Do(o => o= GetItemInfo()));

var mockedvalue = fixture.Create<InfoClass>();

使用第二种方法,在调试时,我可以看到分配给 GetItemInfo 方法内的属性的数据/值。但是,一旦它返回对象并分配给“mockedvalue”,方法内部生成的数据就会消失,并生成没有自定义逻辑的新模拟数据。

我的逻辑有问题吗?或者我错过了什么?

c# xunit autofixture
2个回答
0
投票

对我来说,你的

InfoClass
看起来好像有额外的创建逻辑,所以我会将该逻辑分离到某种
InfoClassHelper
类中,如下所示:

public class InfoClassHelper
{
    private static Dictionary<string, List<string>> ItemsMap = new Dictionary<string, List<string>>
    {
        { Constants.ITEMCATEGORY_ED, new List<string> { "XYZ", "IDASAS" } },
        { Constants.ITEMCATEGORY_ERANDRE, new List<string> { "SAS", "PQR" } }
    };

    public static string[] AvailableReports { get; } = ItemsMap.Values.SelectMany(x => x).ToArray();

    public static string GetInfoIdBasedOnReport(string infoId)
    {
        if (!AvailableReports.Contains(infoId)) return null;

        return ItemsMap
            .First(x => x.Value.Contains(infoId, StringComparer.OrdinalIgnoreCase))
            .Key;
    }
}

然后使用这个类,使用 AutoFixture 创建

InfoClass
的对象会更容易,如下所示:

var autoFixture = new Fixture();

var index = new Random().Next(InfoClassHelper.AvailableReports.Length);
var randomReportId = InfoClassHelper.AvailableReports[index];
var infoClass = autoFixture
    .Build<InfoClass>()
    .With(x => x.InfoID, InfoClassHelper.GetInfoIdBasedOnReport(randomReportId))
    .Create();

编辑

阅读评论后,您可以像下面这样实现工厂类:

public class InfoClassFactory
{
    private static Dictionary<string, List<string>> ItemsMap = new Dictionary<string, List<string>>
    {
        { Constants.ITEMCATEGORY_ED, new List<string> { "XYZ", "IDASAS" } },
        { Constants.ITEMCATEGORY_ERANDRE, new List<string> { "SAS", "PQR" } }
    };

    public static string[] AvailableReports { get; } = ItemsMap.Values.SelectMany(x => x).ToArray();

    private static string CreateInfoClass(string infoId)
    {
        return new InfoClass
        {
            EVDetails = GetEVDetailsBasedOnReport(infoId),
            EDDetails = GetEDDetailsBasedOnReport(infoId),
            EVDetails = GetInfoIdBasedOnReport(infoId),
        }
    }

    private static string GetEDDetailsBasedOnReport(string infoId)
    {
        // logic here
    }

    private static string GetEVDetailsBasedOnReport(string infoId)
    {
        // logic here
    }

    private static string GetInfoIdBasedOnReport(string infoId)
    {
        if (!AvailableReports.Contains(infoId)) return null;

        return ItemsMap
            .First(x => x.Value.Contains(infoId, StringComparer.OrdinalIgnoreCase))
            .Key;
    }
}


0
投票

换个角度思考,更多地从单元测试的角度来看,我用

ICUstomization
修改了你的解决方案。这是我的实现方式:

public class InfoClassCustomization : ICustomization
{
    private readonly Dictionary<string, List<string>> ItemsMap;

    public InfoClassCustomization(Dictionary<string, List<string>> ItemsMap)
    {
        this.ItemsMap = ItemsMap;
    }

    public void Customize(IFixture fixture)
    {
        // Flatten your dictionary to a list of tuples
        var keyValueList = ItemsMap.SelectMany(kvp => kvp.Value.Select(v => (Key: kvp.Key, Value: v))).ToList();

        // Pick a random tuple
        var InfoIdPair = keyValueList[new Random().Next(keyValueList.Count)];

        // Set the ReportID from the randomly selected tuple
        var reportID = InfoIdPair.Value;
        var edDetails = JsonConvert.SerializeObject(fixture.Create<EdDetails>());
        var evDetails = JsonConvert.SerializeObject(fixture.Create<EvDetails>());

        fixture.Customize<InfoClass>(c =>
            {
                var composer = c.With(x => x.ReportID, reportID);
                // Set the correct property based on the key of the randomly selected tuple
                switch (InfoIdPair.Key)
                {
                    case Constants.ITEMCATEGORY_ED:
                        return composer.With(x => x.EDDetails, edDetails);
                    case Constants.ITEMCATEGORY_ERANDRE:
                        return composer.With(x => x.EVDetails, evDetails);
                    default:
                        return c;
                }
            });
    }
}

这是示例单元测试(我认为您错过了

Fixture
Customize(new InfoClassCustomization(dict));
的电话):

public class UnitTest1
{
    [Fact]
    public void Test1()
    {
        // Arrange
        var dict = new Dictionary<string, List<string>>
        {
            { Constants.ITEMCATEGORY_ED, new List<string> { "XYZ", "IDASAS" } },
            { Constants.ITEMCATEGORY_ERANDRE, new List<string> { "SAS", "PQR" } }
        };

        var autoFixture = new Fixture()
            .Customize(new InfoClassCustomization(dict));

        // Act
        var infoClass = autoFixture.Create<InfoClass>();

        // Assert
        var possibleReportIds = dict.Values.SelectMany(x => x);

        Assert.NotNull(
            possibleReportIds
                .FirstOrDefault(x => x == infoClass.ReportID));
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.