Jon
Jon

Reputation: 40032

Setting mock property via Setup causes 'Expression is not a method invocation'

I have the below code where my Mock interface has a Recorder property which is a class.

I then try to set a property on that class but I get a Expression is not a method invocation error. Could you help?

The error is at runtime when it goes to try an set the enum property. It throws an ArgumentException with the below stack trace:

at Moq.ExpressionExtensions.ToMethodCall(LambdaExpression expression)
   at Moq.Mock.<>c__DisplayClass1c`2.<Setup>b__1b()
   at Moq.PexProtector.Invoke[T](Func`1 function)
   at Moq.Mock.Setup[T,TResult](Mock mock, Expression`1 expression, Func`1 condition)
   at Moq.Mock`1.Setup[TResult](Expression`1 expression)

Thanks

//Works
var mock = new Moq.Mock<IEngine>(); 
//Works
mock.Setup(x => x.Recorder).Returns(new Moq.Mock<Recorder>().Object);  
//Fails on the next line assigning a property value!!!
mock.Setup(x => x.Recorder.RunState).Returns(Recorder.eRunStates.Play);  

UPDATE - I have found that RunState is not a property but a field/member which is a enum

Upvotes: 12

Views: 20934

Answers (4)

Jon Skeet
Jon Skeet

Reputation: 1500525

I think you should be returning the mock you've created in the first part for the second part:

var mockRecorder = new Moq.Mock<Recorder>();
mock.Setup(x => x.Recorder).Returns(mockRecorder.Object);
mockRecorder.Setup(x => x.RunState).Returns(Recorder.eRunStates.Play);

That's just a guess, without having used Moq myself - but it makes sense.

However, this looks like it's going to end up being fairly fragile. You might want to consider using a fake instead here - for at least one of the objects, if not both.

EDIT: Looking at the documentation, an alternative would be:

// Moq will set up the hierarchy for you...
mock.Setup(x => x.Recorder.RunState).Returns(Recorder.eRunStates.Play);

Upvotes: 5

Jon
Jon

Reputation: 40032

I have found that creating a mock of Recorder and then assigning values to the mock object seems to fix the issue. Not sure if that is the correct way to do things though.

var mockRecorder = new Moq.Mock<Recorder>();
mockRecorder.Object.RunState = Recorder.eRunStates.Play;

Upvotes: 8

Ciaran
Ciaran

Reputation: 551

If you use SetupGet on the mocks rather than setup this will work

var mockRecorder = new Moq.Mock<Recorder>();
mock.SetupGet(x => x.Recorder).Returns(mockRecorder.Object);
mockRecorder.SetupGet(x => x.RunState).Returns(Recorder.eRunStates.Play);

Upvotes: 4

Trev
Trev

Reputation: 11

You need to do the setup on your Mock object to configure the RunState property instead.

var mockRecorder = new Mock<Recorder>();
mockRecorder.Setup(x => x.RunState).Returns(eRunStates.Play);

mock.Setup(x => x.Recorder).Returns(mockRecorder.Object);

EDIT: FYI, you need to do all the setup on the mock before accessing the .Object property as the object is created at this point and further setups cannot happen.

Also, a couple of suggestions, it looks like your enum for run states is nested in your recorder class, I'd move it into a separate class & additionally drop the 'e' prefix.

Upvotes: 1

Related Questions