Ever introduced an extra parameter on a method, tracked all its usages and made the compiler happy by providing a corresponding extra argument in all the calls of that method?
Sure you have. Either that or removed a parameter from a method.
You just change the signature of the method, adding the new parameter. Then you track down all usages of that method, using “Find all usages” in Visual Studio, or simply by compiling and working through the list of errors.
When you commit it, you would get a a diff much like:
-internal virtual int MethodWithParameterChange(string someString, bool someBool) +internal virtual int MethodWithParameterChange(string someString, int someInt, bool someBool)
internal int MethodUsingInstance() { - var x = _instance.MethodWithParameterChange("this is a string", true); + var x = _instance.MethodWithParameterChange("this is a string", 5, true); return x * 2; }
var instanceToUse = Mock.Of<ClassToMock>(); Mock.Get(instanceToUse) .Setup(instance => instance.MethodWithParameterChange( - It.IsAny<string>(), It.IsAny<bool>())) + It.IsAny<string>(), It.IsAny<int>(), It.IsAny<bool>()))
No biggie. Compiler is happy again. You’re good to go. It compiles. Let’s ship.
Uh oh. Wait. Maybe not so fast. You have unit tests after all. So let’s run these first. You know, because, well, you never know.
Ouch. Failures.
Now, how the heck did that happen?
You just changed the method signature and the calls. Yes, the arguments are passed, but you didn’t change the code in the method yet, so it should still behave as before. Those tests have no right to start misbehaving. They all should still pass. Darn it.
System.Reflection.TargetParameterCountException : Parameter count mismatch.
Huh?
You tracked all the calls. The compiler was happy!
You didn’t go anywhere near reflection.
When you debug one of the tests, sure enough, the exception is raised at a line with a call to the method you just changed. The number of parameters passed to the call is absolutely spot on.
Going back up the call stack doesn’t help much. That just shows the act part of your test was being executed when the exception occurred.
Mystified
What the blazes is going on then?
Well, the method is called on a field member holding an instance of a different class.
The test must have injected that, so it must have been mocked in the test.
Sure enough. Exactly where it should be. In the arrange part of the test, right above the act part.
[TestMethod] public void MethodUsingInstance_ShouldReturn_DoubleTheValueOfItsInstance() { // Arrange var instanceToUse = Mock.Of<ClassToMock>(); Mock.Get(instanceToUse) .Setup(instance => instance.MethodWithParameterChange(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<bool>())) .Returns((string s, bool b) => 23); var cut = new ClassUnderTest(instanceToUse); // Act var returned = cut.MethodUsingInstance(); // Assert Assert.AreEqual(46, returned); }
The Setup looks absolutely fine. No wonder. If it hadn’t, the compiler would have complained loudly.
Scratches head.
Turns to trusted friend Google. S/he comes up with interesting information, but it doesn’t get you much further.
Then, suddenly, out of the blue, you have a spark of inspiration.
That’s it!
The Setup
call for the mocked instance only tells the mock for which method it has to return a value. It’s the Returns
call that tells the mock what to do when the class under test actually calls that mocked method.
When you check it. Yup. The lambda in the Returns
call still has the old number of parameters.
[TestMethod] public void MethodUsingInstance_ShouldReturn_DoubleTheValueOfItsInstance() { // Arrange var instanceToUse = Mock.Of(); Mock.Get(instanceToUse) .Setup(instance => instance.MethodWithParameterChange( It.IsAny (), It.IsAny (), It.IsAny ())) .Returns((string s, bool b) => 23); var cut = new ClassUnderTest(instanceToUse); // Act var returned = cut.MethodUsingInstance(); // Assert Assert.AreEqual(46, returned); }
Oh, the joys of reflection and run time errors!
Tip
If you don’t need the returned value to use any of the argument values for the parameters of the mocked method, you can use empty brackets. Doing it like this ensures that the tests will not start failing when you change the number of parameters.
.Returns(() => 23);
What reflection gotcha’s have had you scratching your head?
Wow.. you are a lifesaver
🙂 glad it helped.