Moq and Find Expressions - Null Reference Exception - c#

I'm fiddling with Moq and I can't figure out why the following proof-of-concept snippet is throwing a NullReferenceException when accessing mock.Object.SingleOrDefault:
var myObject = new MyObject() { Id = 1, Name = "Name" };
var mock = new Mock<MyInterface<MyObject>>();
mock
.Setup(t => t.Find(It.IsAny<Expression<Func<MyObject, bool>>>()))
.Returns(myObject);
var mySecondObject = mock.Object.SingleOrDefault(a => true);
Assert.AreEqual(myObject, mySecondObject);
Any clues?

I'm assuming your MyInterface inherits IQueryable.
Then, when you call SingleOrDefault on that, it calls IQueryable.Provider property on the instance provided.
Since you have not mocked IQueryable.Provider property, it returs null from a mock, so you've got a NullReferenceException.
Try to set MockBehavior.Strict to see if that's the cause.

Related

Moq - Generic set up don't return expected value

I am new to Moq, here I have two setups.
var settingsMock = new Mock<IECSConfigSettings>();
settingsMock.Setup(m => m.GetRootValue("ss", It.IsAny<string>())).Returns("aaa");
And another one is
private static void BuildSettingCacheForMock<T>(Mock<IECSConfigSettings> mock, string key, T value)
{
mock.Setup(m => m.GetRootValue<T>(key, It.IsAny<T>())).Returns(value);
mock.Setup(m => m.GetValue<T>(key, It.IsAny<T>())).Returns(value);
mock.Setup(m => m.TryGetValue<T>(key, out value)).Returns(true);
mock.Setup(m => m.TryGetRootValue<T>(key, out value)).Returns(true);
}
var settingsMock = new Mock<IECSConfigSettings>();
BuildSettingCacheForMock<string>(settingsMock, "sss", "aaa1");
Finally when I invoke both of them
var s1 = new SettingsETag(settingsMock.Object, etag: "ETag", null).Settings.GetRootValue("ss"); // gives "aaa"
var s2 = new SettingsETag(settingsMock.Object, etag: "ETag", null).Settings.GetRootValue("sss"); // gives null
Interface details
Interface code
I do not know what is the differences between these and why the result for the second one is null. Appreciate your thoughts on this!
You have set up a return value for the specific call settingsMock.Setup(m => m.GetRootValue("ss", It.IsAny<string>())).Returns("aaa"); - that is when called with the key "ss", no other values. The second call does not have a set up return value, so you get the default return value null.
In general I think it helps to create mocks with MockBehavior.Strict. This gives you information about unexpected calls when running your unit tests. The constructor looks like this;
var settingsMock = new Mock<IECSConfigSettings>(MockBehavior.Strict);
If you want to return the same value regardless of the parameter value you can use It.IsAny<string>() for the first parameter too.
settingsMock.Setup(m => m.GetRootValue(It.IsAny<string>(), It.IsAny<string>())).Returns("aaa");

Moq - passing arguments from setup() to returns()

I am not sure how to pass arguments from Setup() to Returns() in Moq.
Here is an example:
public static IInterfaceHandler GetInterfaceHandlerMoq()
{
// Defining the moq
var moq = new Mock<IInterfaceHandler>();
// Trying to set up a moq using another moq
moq.Setup(m => m.CreateCommunicationInterface(It.IsAny<Guid>(), It.IsAny<string>()))
.Returns((Guid guid, string value) => GetProgrammerMoq(guid, value));
// Return mocked object
return moq.Object;
}
Note that GetProgrammerMoq() is a library that will return another Moq. Here is the code:
public static IProgrammer GetProgrammerMoq(Guid guid, string instName)
{
// Create Moq
var moq = new Mock<IProgrammer>();
// Set up the returnables
moq.Setup(o => o.InstanceName).Returns(programmer + "_" + instName);
moq.Setup(o => o.Guid).Returns(guid);
// Return mocked object
return moq.Object;
}
See here that GetProgrammerMoq() needs its arguments to be set up based on what is passed to CreateCommunicationInterface().
My test then tries to get and use the Moq, but "p" is returned as null (because, I guess, my arguments are not passed properly to Returns()).
Here is a draft of what the test is to look like:
[Fact]
public void DoSomething()
{
IInterfaceHandler test = ProgrammerMoqs.GetInterfaceHandlerMoq();
Guid? g = new Guid();
IProgrammer p = test.CreateCommunicationInterface(g, "test-boff");
...
}
Try this:
var moq = new Mock<IInterfaceHandler>(MockBehavior.Strict);
MockBehavior.Strict: if you get NULLs from Mock, then always try MockBehavior.Strict. When some setup is not prepared, Moq by default returns NULL. But with MockBehavior.Strict, it will throw an exception. Every single attempt to call anything from the mock object, if it lacks proper setup, will throw.
If you get an exception when trying MockBehavior.Strict, then it means that the:
.Setup(m => m.CreateCommunicationInterface(It.IsAny<Guid>(), It.IsAny<string>()))
failed to catch the invocatio, so the mock returned NULL by default.
Why did it fail to catch the invocation? There are several options:
CreateCommunicationInterface may be overloaded and your setup matched another overload that you did not expect
filters (It.IsAny..) didn't match the actual arguments
(..)
Klaus Gütter noted in the comments about the difference of Guid and Guid?. In fact, the filter you are using is It.IsAny() while in the test you pass:
Guid? g = new Guid();
g is not an object of type Guid, it's Nullable<Guid>, hence the filter looking for any-Guid did not match. The code compiled, because the result of the expression It.IsAny<Guid>() fits Guid? wanted by the method, but still the types don't match.
If you try It.IsAny<Guid?>() it will probably match fine and return what you wanted.
moq.Setup(m => m.CreateCommunicationInterface(It.IsAny<Guid?>(), It.IsAny<string>()))
.Returns((Guid? guid, string value) => GetProgrammerMoq(guid, value));

Unit Testing error "Object reference not set to an instance of an object."

In my controller I want to test if the controller is calling the repository method.
Here is the method in controller
[HttpGet]
public ActionResult GetModulePropertyName(string moduleTypeValue)
{
var temp = _modulerepository.GetModuleKindPropertyNames(moduleTypeValue);
IList<Property> model = temp
.Select(item => new Property {Name = item})
.ToList();
return PartialView("GetModulePropertyName",model);
}
And here is the test method
[TestMethod]
public void GetModulePropertyName_Action_Calls_GetModuleKindPropertyNames()
{
_mockRepository.Stub(x => x.GetModuleKindPropertyNames(Arg<string>.Is.Anything));
_controller.GetModulePropertyName(Arg<string>.Is.Anything);
_mockRepository.AssertWasCalled(x=>x.GetModuleKindPropertyNames(Arg<string>.Is.Anything));
}
It throws an error saying
Test method AdminPortal.Tests.ModuleControllerTests.GetModulePropertyName_Action_Calls_GetModuleKindPropertyNames threw exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Linq.Queryable.Select(IQueryable`1 source, Expression`1 selector)
at AdminPortal.Areas.Hardware.Controllers.ModuleController.GetModulePropertyName(String moduleTypeValue) in ModuleController.cs: line 83
at AdminPortal.Tests.ModuleControllerTests.GetModulePropertyName_Action_Calls_GetModuleKindPropertyNames() in ModuleControllerTests.cs: line 213
I'm using RhinoMock as mocking tool.
Can someone help with what mistake i'm making?
After stubbing the method use Return to indicate what should it return, for example:
_mockRepository
.Stub(x => x.GetModuleKindPropertyNames(Arg<string>.Is.Anything))
.Return(Enumerable.Empty<string>().AsQueryable());
Also, change this line:
_controller.GetModulePropertyName(Arg<string>.Is.Anything);
to this:
_controller.GetModulePropertyName(string.Empty);
As the exception explains - Arg is only to be used in mock definitions.
You don't have a return on your stub.
_mockRepository.Stub(x => x.GetModuleKindPropertyNames(Arg<string>.Is.Anything));
without that return, this line will be running a lambda against a null reference
IList<Property> model = temp.Select(item => new Property {Name = item}).ToList();
so:
_mockRepository.Stub(x => x.GetModuleKindPropertyNames(Arg<string>.Is.Anything)).Return(new Module[]{}); // set some return data here

Mocking indexed property

I am writing unit tests using Moq. I have created a mock object. Now when i try to mock its property i am getting error "An expression tree may not contain an indexed property"
here is my code.
public Node GetNode(IMyInterface interface, string itemName)
{
return interface.Items[itemName];
}
Here is the unit test
var expected = new Node();
var itemName = "TestName";
var mock = new Mock<IMyInterface>();
mock.Setup(f => f.Items[itemName]).Returns(expected);
var target = new MyClass();
var actual = target.GetNode(mock.Object, itemName);
Assert.AreEqual(expected, actual);
This line is giving me error.
mock.Setup(f => f.Items[itemName]).Returns(expected);
How can i moq this function.
Interface was a COM object and there were get function, so instead of directly accessing property using indexer use get function,
mock.Setup(f => f.get_Items(itemName)).Returns(expected);
Using Moq in ASP.NET Core 2.2, the get_Items setup does not work. But this does:
Mock<IConfiguration> configuration = new Mock<IConfiguration>();
configuration.Setup(x => x[key]).Returns(value);

Moq - Object type casting and comparison

I'm trying to use an IHTMLSelectElement with Moq. My code is something like:
// create a select element
var selectElem = new Mock<IHTMLSelectElement>(MockBehavior.Strict);
// set the select element
selectElem.Setup(f => f.length).Returns(20);
selectElem.Setup(f => f.name).Returns("selectElem");
// get the object
IHTMLSelectElement ihse = selectElem.Object;
Then in my production code method, I do:
var selectEle = (element as mshtml.IHTMLSelectElement);
if (selectEle != null)
{
My problem is that the type cast doesn't work because when using Moq the type is actually:
Castle.Proxies.IHTMLSelectElementProxy_1
Casting this to IHTMLSelectElement returns a null object.
Any idea on how I can make this work?
You'll need to make it injectable, either a property or a input parameter to a method, depending on the code. And then you can inject the with the MOQ object. The two lines of your code should not be doing the "as", it should be dealing with the correct type of element directly.

Categories

Resources