5

I used Fluxor for state management in in one of the razor components in my Blazor app and I'm not really sure how to set it up for testing with Moq. I can't just assing a value to students because it's an init only property. Is there a specific way to set up Fluxor for testing?

code

var teacherService = new Mock<ITeacherService>();
            var  localStorage = new Mock<ILocalStorageService>();
            var  studentService = new Mock<IStudentService>();
            var  toastService = new Mock<IToastService>();
            var  NavigationManager = new Mock<NavigationManager>();
            var  Dispatcher = new Mock<IDispatcher>();
            var actionSubscriber = new Mock<IActionSubscriber>();

            var StudentsState = new Mock<IState<StudentsState>>();
            var TeacherState = new Mock<IState<TeacherState>>();

            // Help here
            StudentsState.Setup(t => t.Value.Students = )
Egil Hansen
  • 15,028
  • 8
  • 37
  • 54
  • This is a MOQ question, not really a bUnit question. Anyway, take a look at https://github.com/Moq/moq4/wiki/Quickstart#properties. It explains how to define return values for properties, e.g. `mock.Setup(foo => foo.Name).Returns("bar");` – Egil Hansen May 22 '21 at 09:39
  • Here are some examples of how to write tests for rendering of components, reducers and dispatching of actions with Blazor and Fluxor: https://github.com/mrpmorris/Fluxor/issues/397#issuecomment-1517884399 – eharbitz Apr 23 '23 at 09:56

2 Answers2

5

I was stuck on this for quite a while, but went looking through Fluxor's glitter community and found something that might help! I understand that you'd like to test using Moq, but perhaps what I found might be easier/suit your purposes for testing Fluxor components!

Instead of using Moq, I followed this format and managed to get something working:

Test.cs

public class InhousePetOwnerStateTests
{
    private readonly IServiceProvider ServiceProvider;
    private readonly IStore Store;
    private readonly IState<InhousePetOwnerListState> State;
    public InhousePetOwnerStateTests()
    {
        var services = new ServiceCollection();
        services.AddFluxor(x =>
        x
            .ScanAssemblies(GetType().Assembly)
            .ScanTypes(typeof(InhousePetOwnerListState), typeof(Reducers)));
    
        ServiceProvider = services.BuildServiceProvider();
        Store = ServiceProvider.GetRequiredService<IStore>();
        State = ServiceProvider.GetRequiredService<IState<InhousePetOwnerListState>>();
        Store.InitializeAsync().Wait();
    }

    [Fact]
    public void SetInhousePetOwnerList_Empty_StateUpdated()
    {
        Store.Dispatch(new SetInhousePetOwnerListAction(new()));
        Assert.Empty(State.Value.InhousePetOwners);
    }
        
    [Fact]
    public void SetInhousePetOwnerList_MultipleInhouseOwners_StateUpdated()
    {
        List<InhousePetOwner> inhousePetOwners = new List<InhousePetOwner>
        {
            new InhousePetOwner()
            {
                InhousePetOwnerId = 1,
                PetOwnerId = 1,
                TimeIn = System.DateTime.Now
            },
            new InhousePetOwner()
            {
                InhousePetOwnerId = 2,
                PetOwnerId = 2,
                TimeIn = System.DateTime.Now
            },
        };
        Store.Dispatch(new SetInhousePetOwnerListAction(inhousePetOwners));
        Assert.Equal(2, State.Value.InhousePetOwners.Count);
    }
}

In the constructor, we create a service collection and add Fluxor (just like you would to your Startup.cs), then scan for assemblies.

In my code above, I scanned for the Reducers and the State. Please refer to my directory structure here (you'll get an idea of how I decided what to include).

Actions.cs

public class SetInhousePetOwnerListAction
{
    public List<InhousePetOwner> InhousePetOwners { get; }
    public SetInhousePetOwnerListAction(List<InhousePetOwner> inhousePetOwners)
    {
        InhousePetOwners = inhousePetOwners;
    }
}

Reducers.cs (scanned in the constructor)

public static class Reducers
{
    [ReducerMethod]
    public static InhousePetOwnerListState ReduceSetInhousePetOwnerListAction(InhousePetOwnerListState state, SetInhousePetOwnerListAction action)
    {
        return new InhousePetOwnerListState(inhousePetOwners: action.InhousePetOwners);
    }
}

InhousePetOwnerState.cs (scanned in the constructor)

public class InhousePetOwnerListState
{
    public List<InhousePetOwner> InhousePetOwners { get; }

    private InhousePetOwnerListState() { }

    public InhousePetOwnerListState(List<InhousePetOwner> inhousePetOwners)
    {
        InhousePetOwners = inhousePetOwners;
    }
}

What this solution does is it allows you to inject all your Fluxor states, actions and reducers, enabling you to call them as you normally would - I've found it pretty easy to test this way!

Monky Monk
  • 51
  • 1
  • 6
  • From your Reducers.cs cs file, I can't see a definition of `Reducers` type which you have used to scan in the constructor of `InhousePetOwnerStateTests`. I'm talking about this line `.ScanTypes(typeof(InhousePetOwnerListState), **typeof(Reducers)**));` – Robert Mrobo Dec 12 '21 at 21:23
  • 1
    @RobertMrobo Thanks for the comment! I've updated the file above to reflect the definition of Reducers.cs; hopefully, it's clearer now! – Monky Monk Dec 14 '21 at 15:48
  • Just a note for anyone using `Effects` that rely on services you wrote yourself: You'll need to register mock services with the test context so the IoC can create them. If you don't, the IoC will attempt to initialize fluxor and fail because it can't find the custom services. – RubberDuck Jan 27 '22 at 12:25
1

The way I did this was to use the set up to return a whole state record, configured the way that I wanted it.

So if you have a IState where StudentsState is readonly

You can do:

       var studentState = new Mock<IState<StudentsState>>();
            studentState.Setup(s => s.Value)
             .Returns(
                new StudentsState 
                  { 
                      Students = new List<StudentRecord> 
                         { ... }
                   });
Kaine
  • 1,285
  • 2
  • 16
  • 30