Search code examples
c#unit-testingmoqexpression-bodied-members

How to mock an expression-bodied member that doesn't have a getter


Context

I have a class that has an expression-bodied member:

public MyState 
{
    public List<Thing> Things { get; set; }
    public double SomeValue => Things.sum(thing => thing.Value);
    public double MyProperty { get; set; }
}

I need to write a unit test for a method Apply() whose signature looks like this:

public OtherClass 
{
    public override MyList Apply(MyState state) {
        state.MyProperty = state.SomeValue;
        return null;
    }
}

Initial Attempt

public void Test()
{
    var mock = new Mock<MyState>();
    mock.SetupGet(mock => mock.SomeValue).Returns(100.0);

    var otherClass = new OtherClass();
    otherClass.Apply(mock.Object);

    ...
}

This resulted in an error stating that SomeValue is a non-overridable member.

Second Attempt

I then implemented an interface IMyState like so:

public interface IMyState 
{
    double SomeValue { get; set; }
    double MyProperty { get; set; }
}

and modified my test to implement that interface for the mock. This resulted in an error calling the Apply() method that I want to test, because Apply takes an instance of MyState rather than IMyState:

public void Test()
{
    var mock = new Mock<MyState>().As<IMyState>();        // implement interface here
    mock.SetupGet(mock => mock.SomeValue).Returns(100.0);

    var otherClass = new OtherClass();
    otherClass.Apply(mock.Object);                       // mock.Object is of class IMyState

    ...
}

I'm guessing this is because MyState does not actually implement IMyState. Is there some way I can mock this property to return a specified value without changing MyState to implement IMyState (or declare SomeValue as virtual, which I've seen in other StackOverflow posts)? I'd prefer not to make changes to the existing code solely for the purpose of testing.

Third Attempt

I tried casting the mock back to MyState before passing it to Apply(), but SomeValue then returned 0 rather than 100.0 as I set the mock up to return:

public void Test()
{
    var mock = new Mock<MyState>().As<IMyState>();
    mock.SetupGet(mock => mock.SomeValue).Returns(100.0);

    var otherClass = new OtherClass();
    otherClass.Apply((MyState)mock.Object);  // after casting, mock.Object.SomeValue returns 0

    ...
}

Solution

  • If you are trying to test Apply method, and you have dependency on MyState which you want to mock - and since you don't want to change it - answer is simple - you can't.

    Only interfaces or virtual members. End of story.

    But I guess the problem lies in creation of List<Thing>. I would use AutoFixture nuget package for that. Then it would become really simple:

    var fixture = new Fixture();
    var things = fixture.Build<Thing>()
        .With(x => x.Value, 3) // for each created Thing set the Value to 3
        .CreateMany(3) // create 3 things
        .ToList();
    var state = fixture.Create<MyState>();
    state.Things = things;
    
    // Now you know that state.SomeValue should equal to 9.
    // To further simplify, you can always sat Value on 
    // Thing to 0 in the test, then you know state.SomeValue 
    // would be always 0, regardless of number of items.