起源是,用MemberData进行封装的Test方法,在Visual Studio 的Test Explorer中只显示为1个。本来,这个显示问题没啥问题,问题是,当你其中一个测试用例出错,而你又想进行Debug的时候,在Test Explorer中运行该方法,实际上会遍历所有的调用,是的Debug过程异常麻烦。

于是去找Github, StackOverflow, 果不其然,c# - MemberData tests show up as one test instead of many - Stack Overflow,有人遇到了同样的问题。

遵循上述Question的解决方案中的做法,实现IXunitSerializable,然而,得到的其实,Visual Studio的Test Explorer再也无法显示了。

示例代码:

    public DayOfWeek Dow { get; private set; }
    public List<UserHabitRecord> RecordList { get; private set; }
    public int CompleteCondition { get; private set; }
    public int RecordCount { get; private set; }
    public List<DateTime> TargetRuleDateList { get; private set; }
    public Guid InstanceID { get; private set; }

    public UserHabitRecordsControllerTestData_WeekNoOfCount()
    {
        this.RecordList = new List<UserHabitRecord>();
        this.TargetRuleDateList = new List<DateTime>();
        this.InstanceID = Guid.NewGuid();
    }
    public UserHabitRecordsControllerTestData_WeekNoOfCount(DayOfWeek dow,
        List<UserHabitRecord> records, int completeCondition, int recordCount,
        List<DateTime> arTargetRuleDate) : this()
    {
        this.Dow = dow;
        this.RecordList.AddRange(records);
        this.CompleteCondition = completeCondition;
        this.RecordCount = recordCount;
        this.TargetRuleDateList.AddRange(arTargetRuleDate);
    }

    public void Deserialize(IXunitSerializationInfo info)
    {
        Dow = info.GetValue<DayOfWeek>("Dow");
        RecordList = info.GetValue<List<UserHabitRecord>>("RecordList");
        CompleteCondition = info.GetValue<int>("CompleteCondition");
        RecordCount = info.GetValue<int>("RecordCount");
        TargetRuleDateList = info.GetValue<List<DateTime>>("TargetRuleDateList");
        InstanceID = Guid.Parse(info.GetValue<String>(nameof(InstanceID)));
    }

    public void Serialize(IXunitSerializationInfo info)
    {
        info.AddValue(nameof(Dow), Dow, typeof(DayOfWeek));
        info.AddValue("RecordList", RecordList, typeof(List<UserHabitRecord>));
        info.AddValue("CompleteCondition", CompleteCondition, typeof(int));
        info.AddValue("RecordCount", RecordCount, typeof(int));
        info.AddValue("TargetRuleDateList", TargetRuleDateList, typeof(List<DateTime>));
        info.AddValue(nameof(InstanceID), InstanceID.ToString("N"), typeof(String));
    }

不死心,用dotnet CLI的方法尝试了一下,原来是所有的测试都无法Discover了:

dotnet test -t

上述命令无法完成,只能Ctrl+C强制退出。

再去xUnit上去翻文档,再去Github, Stackoverflow上翻Issue,再去Google上查找IXunitSerializable的示例代码,但是没有结果。

又去下载了最新的Visual Studio 2022,问题依旧。

甚至都准备换MSTest或者NUnit框架了,只是看看之前写的那么多Test Cases,想想这种换Test Framework的苦力获,只得作罢。

最终狠下心来,彻底搞明白这个问题然后彻底解决之。于是突然想到,这个过程应该是卡在Serialization的方法里面,应该是List 以及其中所有Object都应该支持Serializable。当然也可以修改所有关联的Class以达到Serializatable的效果。但是,可能换个Serialization的实现会简单。譬如,.Net框架中JSON分析。

于是操刀动手:

    public DayOfWeek Dow { get; set; }
    public List<UserHabitRecord> RecordList { get; set; }
    public int CompleteCondition { get; set; }
    public int RecordCount { get; set; }
    public List<DateTime> TargetRuleDateList { get; set; }

    public void Deserialize(IXunitSerializationInfo info)
    {
        String val = info.GetValue<String>("Value");
        UserHabitRecordsControllerTestData_WeekNoOfCount other = JsonSerializer.Deserialize<UserHabitRecordsControllerTestData_WeekNoOfCount>(val);

        // CaseID = other.CaseID;
        Dow = other.Dow;
        CompleteCondition = other.CompleteCondition;
        RecordCount = other.RecordCount;
        if (other.RecordList.Count > 0)
            RecordList.AddRange(other.RecordList);
        if (other.TargetRuleDateList.Count > 0)
            TargetRuleDateList.AddRange(other.TargetRuleDateList);
    }

    public void Serialize(IXunitSerializationInfo info)
    {
        String val = JsonSerializer.Serialize(this);
        info.AddValue("Value", val, typeof(String));
    }

BINGO!!!

踏破铁鞋无觅处啊,看到Test Explorer中那跳动数字,差点从椅子上跳起来。

中间还有个小插曲,虽然Test Explorer显示正确,但是测试始终失败。打了个断点一瞧,Deserialize的结果一直为空。原因是Property设置为了private set了:

    public DayOfWeek Dow { get; private set; }
    public List<UserHabitRecord> RecordList { get; private set; }
    public int CompleteCondition { get; private set; }
    public int RecordCount { get; private set; }
    public List<DateTime> TargetRuleDateList { get; private set; }

收工。

是为之记。 Alva Chien 2021.11.15