0%

基于Abp的扩展模块实现第3例 - 状态管理(测试)

其他系列博文

背景

在上一篇博文中,我们建立了状态切换/处理源状态/目标状态的解耦,实现了一个基于Abp的状态管理模块,在这一篇中博主将对该机制编写单元测试。

目标

  • 对状态定义与状态处理两个核心功能进行测试。

思路

在创建测试状态类时,覆盖以下情形:

  • 父子状态层次结构
  • 状态切换分支
  • 多对多切换

编码

  1. 创建测试状态定义类TestStateDefinitionProvider,与状态名称静态类TestStateNames

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    public class TestStateDefinitionProvider : StateDefinitionProvider
    {
    public override void Define(IStateDefinitionContext context)
    {
    var deviceGroup = context
    .AddGroup(TestStateNames.RootGroup)
    .AddGroup(TestStateNames.DeviceGroup);

    var onlineGroup = deviceGroup.AddGroup(TestStateNames.OnlineGroup);
    var normalGroup = onlineGroup.AddGroup(TestStateNames.NormalGroup);
    var faultGroup = onlineGroup.AddGroup(TestStateNames.FaultGroup);

    var normalOpenState = normalGroup.AddState(TestStateNames.NormalOpenState);
    var normalCloseState = normalGroup.AddState(TestStateNames.NormalCloseState);
    var faultOpenState = faultGroup.AddState(TestStateNames.FaultOpenState);
    var faultCloseState = faultGroup.AddState(TestStateNames.FaultCloseState);

    var offlineState = deviceGroup.AddState(TestStateNames.OfflineState);

    onlineGroup
    .On(TestStateNames.OfflineTerm)
    .TransitTo(offlineState);

    offlineState
    .On(TestStateNames.NormalCloseTerm)
    .TransitTo(normalCloseState)
    .On(TestStateNames.FaultCloseTerm)
    .TransitTo(faultCloseState)
    .On(TestStateNames.NormalOpenTerm)
    .TransitTo(normalOpenState)
    .On(TestStateNames.FaultOpenTerm)
    .TransitTo(faultOpenState);

    normalOpenState
    .On(TestStateNames.NormalCloseTerm)
    .TransitTo(normalCloseState)
    .ReverseOn(TestStateNames.NormalOpenTerm)
    .On(TestStateNames.FaultOpenTerm)
    .TransitTo(faultOpenState)
    .ReverseOn(TestStateNames.NormalOpenTerm);

    normalCloseState
    .On(TestStateNames.FaultCloseTerm)
    .TransitTo(faultCloseState)
    .ReverseOn(TestStateNames.NormalCloseTerm);

    faultOpenState
    .On(TestStateNames.FaultCloseTerm)
    .TransitTo(faultCloseState)
    .ReverseOn(TestStateNames.FaultOpenTerm);
    }
    }

    public static class TestStateNames
    {
    public const string RootGroup = "Root";
    public const string DeviceGroup = "Device";

    public const string OnlineGroup = DeviceGroup + ".Online";
    public const string NormalGroup = OnlineGroup + ".Normal";
    public const string FaultGroup = OnlineGroup + ".Fault";

    public const string NormalOpenState = NormalGroup + ".Open";
    public const string NormalCloseState = NormalGroup + ".Close";
    public const string FaultOpenState = FaultGroup + ".Open";
    public const string FaultCloseState = FaultGroup + ".Close";

    public const string OfflineState = DeviceGroup + ".Offline";

    public const string OfflineTerm = "Offline";
    public const string NormalOpenTerm = "NormalOpen";
    public const string NormalCloseTerm = "NormalClose";
    public const string FaultOpenTerm = "FaultOpen";
    public const string FaultCloseTerm = "FaultClose";
    }
  2. 创建测试类StatesDefinition_Tests测试状态定义功能:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    public class StatesDefinition_Tests : StatesTestBase
    {
    private readonly IStateDefinitionManager _stateDefinitionManager;
    private readonly IConfiguration _configuration;

    public StatesDefinition_Tests()
    {
    _stateDefinitionManager = GetRequiredService<IStateDefinitionManager>();
    _configuration = GetRequiredService<IConfiguration>();
    }

    [Fact]
    public void Should_Register_States_Using_Provider()
    {
    var deviceGroup = _stateDefinitionManager.GetGroupRecursivelyOrNull(TestStateNames.DeviceGroup);
    var onlineGroup = _stateDefinitionManager.GetGroupRecursivelyOrNull(TestStateNames.OnlineGroup);
    var normalGroup = _stateDefinitionManager.GetGroupRecursivelyOrNull(TestStateNames.NormalGroup);
    var faultGroup = _stateDefinitionManager.GetGroupRecursivelyOrNull(TestStateNames.FaultGroup);

    deviceGroup.ShouldNotBeNull();
    onlineGroup.ShouldNotBeNull();
    normalGroup.ShouldNotBeNull();
    faultGroup.ShouldNotBeNull();

    deviceGroup.States.Count.ShouldBe(1);
    onlineGroup.States.Count.ShouldBe(0);
    normalGroup.States.Count.ShouldBe(2);
    faultGroup.States.Count.ShouldBe(2);

    onlineGroup.GetAllStates().Count.ShouldBe(4);

    deviceGroup.States.SelectMany(state => state.Transitions).Count().ShouldBe(4);
    normalGroup.States.SelectMany(state => state.Transitions).Count().ShouldBe(6);
    faultGroup.States.SelectMany(state => state.Transitions).Count().ShouldBe(6);
    }

    [Fact]
    public void Should_Get_Correct_Group_Belongings()
    {
    _stateDefinitionManager.IsGroupBelongTo(TestStateNames.OnlineGroup, TestStateNames.DeviceGroup).ShouldBeTrue();
    _stateDefinitionManager.IsGroupBelongTo(TestStateNames.NormalGroup, TestStateNames.DeviceGroup).ShouldBeTrue();
    _stateDefinitionManager.IsGroupBelongTo(TestStateNames.OnlineGroup, TestStateNames.NormalGroup).ShouldBeFalse();
    _stateDefinitionManager.IsGroupBelongTo(TestStateNames.OnlineGroup, TestStateNames.OnlineGroup).ShouldBeTrue();
    _stateDefinitionManager.IsGroupBelongTo(TestStateNames.OnlineGroup, TestStateNames.OnlineGroup, false).ShouldBeFalse();
    }

    [Fact]
    public void Should_Read_State_Definition_From_Configuration()
    {
    var configuration = _configuration;
    }
    }
  3. 创建测试类StatesHandler_Tests测试状态切换处理功能:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    public class StatesHandler_Tests : StatesTestBase
    {
    private readonly IStateTransitionNotifier _stateTransitionNotifier;
    private readonly Func<IDictionary<StateTransitionDefinition, bool>, string> _postHandleAction;
    private readonly Action<StateTransitionContext> _contextAction;

    public StatesHandler_Tests()
    {
    _stateTransitionNotifier = GetRequiredService<IStateTransitionNotifier>();

    _postHandleAction = handlerContext => handlerContext.Values.Where(value => value).Count().ToString();
    _contextAction = context => context.SetProperty(TestStateTransitionHandlerBase.PostHandleActionKey, _postHandleAction);
    }

    [Fact]
    public async Task Should_Notify_Within_Given_Group_By_Default()
    {
    await _stateTransitionNotifier.NotifyAsync(TestStateNames.DeviceGroup, TestStateNames.NormalCloseTerm, _contextAction);
    RecordLatestStateTransitionHandler.LatestTransition.Count.ShouldBe(3);
    RecordLatestStateTransitionHandler.HandleResult.ShouldBe("3");

    await _stateTransitionNotifier.NotifyAsync(TestStateNames.NormalGroup, TestStateNames.NormalCloseTerm, _contextAction);
    RecordLatestStateTransitionHandler.LatestTransition.Count.ShouldBe(1);
    RecordLatestStateTransitionHandler.HandleResult.ShouldBe("1");
    }

    [Fact]
    public async Task Should_Break_When_All_Transitions_Are_Handled_By_Default()
    {
    await _stateTransitionNotifier.NotifyAsync(TestStateNames.DeviceGroup, TestStateNames.NormalCloseTerm, _contextAction);
    UnReachableStateTransitionHandler.HandleResult.ShouldBeNull();
    }

    [Fact]
    public async Task Should_Not_Handle_If_Notified_Group_Is_Larger()
    {
    await _stateTransitionNotifier.NotifyAsync(TestStateNames.RootGroup, TestStateNames.NormalCloseTerm, _contextAction);
    RecordLatestStateTransitionHandler.LatestTransition.Count.ShouldBe(0);
    RecordLatestStateTransitionHandler.HandleResult.ShouldBeNull();
    }
    }
  4. 测试写完了,我们来跑一下:

测试结果

完美通过。

至此状态管理模块的开发与测试已全部完毕。