New to Moq and this is a very simple example. I want to Mock the call to my "int smokeTest(int a, int b)" method which is used within my method "string getCode(int someID)". Variables for smokeTest are declared and set within getCode. The issue is when I mock the method call to smokeTest I always get a result of "0" within getCode but I want to see my predefined result of "20". The only work around I've found is to overload the method and pass in all variables. However, I do not want to do this because many of the methods I want to test declare and use local variables. What's the best way to test this method using Moq? Thanks
// Example A
public string getCode(int someID)
{
int x = 5;
int y = 5;
int z = _dataAccess.smokeTest(x, y);
return _dataAccess.getCode(someID);
}
// NOTE: Doesn't work as wanted
[Test]
public void test_getCode_TestB()
{
var x = It.IsAny<int>();
var y = It.IsAny<int>();
// NOTE: "20" is not returned, 0 is returned instead because local variables are used
_mockDataAccess.Setup(m => m.smokeTest(x, y)).Returns(20);
_mockDataAccess.Setup(m => m.getCode(234)).Returns("def345");
var result = _businessLogic.getCode(234);
Assert.IsTrue(result == "def345");
}
// Example B
// NOTE: Workaround - pass in variables
public string getCode(int someID, int a, int b)
{
var c = _dataAccess.smokeTest(a, b);
return _dataAccess.getCode(someID);
}
[Test]
public void test_getCode_TestC()
{
var x = It.IsAny<int>();
var y = It.IsAny<int>();
// NOTE: "20" is returned as wanted
_mockDataAccess.Setup(m => m.smokeTest(x, y)).Returns(20);
_mockDataAccess.Setup(m => m.getCode(234)).Returns("def345");
var result = _businessLogic.getCode(234, x, y);
Assert.IsTrue(result == "def345");
}
It will only work if you pass It.IsAny<int>() directly into the moq setup:
_mockDataAccess.Setup(m => m.smokeTest(It.IsAny<int>(), It.IsAny<int>())).Returns(20);
I was browsing moq source code to found out that doing this:
int x = It.IsAny<int>();
int y = It.IsAny<int>();
_mockDataAccess.Setup(m => m.smokeTest(x, y)).Returns(20);
will assigened x and y to be the deafult value of int which is 0, so your setup is equivalent to this:
_mockDataAccess.Setup(m => m.smokeTest(0, 0)).Returns(20);
this setup tells the moq to return 20 if the parameters are 0 and 0, otherwise it will return the deafult value of int which is 0 again. This is not the behavior you intended to create so you should not assigned It.IsAny<int>() into x and y before you pass them into the moq setup.
As #Rob comment indicates It.IsAny<T> is a kind of stub that you can pass into the moq setup, the setup method will know how to deal with it using Expression Trees, but the value that this method actually returns is always the deafult of T. This is why we have to use this method always directly in the moq setup.
Related
I am working on a project that uses Javascript.net (formerly Noesis Javascript .net) as a scripting engine.
How do you add a C# method to the global context in Javascript?
For example, say I have in C#:
public int Foo(int bar)
{
return bar * 2;
}
I want to be able to call in Javascript:
var x = 5;
var y = Foo(x); // y is now 10;
In other libraries such as Jurassic this is possible:
engine.SetGlobalFunction("Foo", new Func<int, int>((a) => a*2));
This example comes from the unit tests:
_context.SetParameter("f", new Func<string,string>((s) => s.ToUpper()));
_context.Run("f('Noesis') == 'NOESIS'").Should().BeOfType<bool>().Which.Should().BeTrue();
I'm working on a project that requires that I use a few Lambdas. I am trying to create one that will have two strings and an int plugged into it, and if the two strings are the same, then the int++. Here is my code:
DelegateName tally = (x, y, z) => { if (x == y) z++; };
I understand how not all code paths return a value but I am not sure what direction to go to fix it. I essentially will run this 12 times in order to add a tally to one of 12 variables if the x string is the same as the y string. It is counting the number of matching strings and adding it to the appropriate variable (the int)
Here is the code that I am trying to replace with a lambda:
if (cellValue.ToString().Substring(0, 1) == "1")
{
variable1++;
}
Was hoping to clean it up by calling tally(cellValue.ToString().SubString(0,1), 1, variable1) instead
Your variable1 is a int (a Value type) so when you pass it as argument, you actually passing its value, so inside your tally lambda there will be another int variable z which is independent from variable1. So, when you do z++ inside tally, your variable1 is not changing.
If your tally is defined near variable1 you may just use it inside your lambda:
var variable1 = 0;
DelegateName tally = (x, y) => { if (x == y) variable1++; };
foreach (var cellValue in cells)
tally(cellValue.ToString().SubString(0,1), "1");
Another way is a LINQ:
using System.Linq;
var variable1 = cells.Select(c => c.ToString().FirstOrDefault()).Count(c => c == '1');
// or even better (thx #RufusL)
var variable2 = cells.Count(c => c.ToString().StartsWith("1"));
Think of a lambda as just shorthand for a regular method. The lambda you provided:
(x, y, z) => { if (x == y) z++; }
translates to:
void IncrementIfEqual(string x, string y, int z)
{
if (x == y) z++;
}
(Lambdas don't have a method name, so I just randomly called the method IncrementIfEqual). You aren't returning any value from inside the method, so the method returns void. Also, the argument z that you pass in gets incremented inside the method, but doesn't have any effect on the value of the original variable passed in. For example, if you called the method you wrote like this...
IncrementIfEqual(cellValue.ToString().Substring(0, 1), "1", variable1)
then variable1 would remain the same value after the call because the method incremented the argument z, which doesn't exist outside the method. You could write the method like this...
int IncrementIfEqual(string x, string y, int z)
{
return (x == y) ? ++z : z;
}
which increments the value z and returns it if x and y are equal, otherwise it just returns the original value passed in. You could then call it like this...
variable1 = IncrementIfEqual(cellValue.ToString().Substring(0, 1), "1", variable1)
In that case, the lambda expression you'd want is this...
(x, y, z) => { return (x == y) ? ++z : z; }
I'm not sure what DelegateName is, but if it has the Invoke() method, your full usage might be something like this...
DelegateName tally = (x, y, z) => { if (x == y) z++; };
variable1 = tally.Invoke(cellValue.ToString().Substring(0, 1), "1", variable1)
I converted a SpecFlow step to depend on StepArgumentTransformation so I would not have to deal with multiple primitive parameters, but could use a single complex parameter, built by a transformation method.
But I learned that I could not remove the primitive parameters from the step, because that would result in a "parameter count mismatch" exception.
[Given("(something (\d) (\d))")]
public void Step(Something something)
{
// Does not work (parameter count mismatch)
}
[Given("(something (\d) (\d))")]
public void Step(Something something, int x, int y)
{
// Works, but is slightly confusing due to unused parameters
}
[StepArgumentTransformation("something (\d) (\d)")]
public Something Transform(int x, int y)
{
var something = new Something(x, y);
return something;
}
How can I get rid of the x and y parameters in the step method?
You have to adapt the regex of the binding.
Here is a concrete example:
[Given("the coordinates are '(.*)'")]
public void Step(Something something)
{
// Does not work (parameter count mismatch)
}
[StepArgumentTransformation("X:(\d)/Y:(\d)")]
public Something Transform(int x, int y)
{
var something = new Something(x, y);
return something;
}
Usage:
Scenario: coordinate system
Given the coordinates are 'X:1/Y:2'
You can find an example here: https://github.com/techtalk/SpecFlow-Examples/tree/master/SpecFlow.Examples.StepArgumentTransformation
The documentation for Step Argument Transformation is here: http://specflow.org/documentation/Step-Argument-Conversions/
Let's consider this code:
public void DoSomething<T>(Func<T> MyFunc)
{
var NewLazyItem = new Lazy<T>(MyFunc);
// do stuff
// use the NewLazyItem
}
And let's say I have a method like this:
public int Add(int a, int b) { return a + b; }
What I would like to achieve is to be able to pass the Add method to the DoSomething method but with parameters along.
Ideally I could pass Add, 2 and 3 and when the NewLazy item gets used, Add(2, 3) gets called.
I tried various ideas, but I can't find a way to make it work.
Unless I'm misunderstanding the question, the simplest way is just to include the parameters as closures for a lambda expression:
var x = 2;
var y = 3;
DoSomething(() => Add(x, y));
Why can't you use a ref or out parameter in a lambda expression?
I came across the error today and found a workaround but I was still curious why this is a compile-time error.
CS1628: Cannot use in ref or out parameter 'parameter' inside an anonymous method, lambda expression, or query expression
Here's a simple example:
private void Foo()
{
int value;
Bar(out value);
}
private void Bar(out int value)
{
value = 3;
int[] array = { 1, 2, 3, 4, 5 };
int newValue = array.Where(a => a == value).First();
}
Lambdas have the appearance of changing the lifetime of variables that they capture. For instance, the following lambda expression causes the parameter p1 to live longer than the current method frame as its value can be accessed after the method frame is no longer on the stack
Func<int> Example(int p1) {
return () => p1;
}
Another property of captured variables is that changes to the variables are also visible outside the lambda expression. For example, the following code prints out 42
void Example2(int p1) {
Action del = () => { p1 = 42; };
del();
Console.WriteLine(p1);
}
These two properties produce a certain set of effects which fly in the face of a ref parameter in the following ways:
ref parameters may have a fixed lifetime. Consider passing a local variable as a ref parameter to a function.
Side effects in the lambda would need to be visible on the ref parameter itself. Both within the method and in the caller.
These are somewhat incompatible properties and are one of the reasons they are disallowed in lambda expressions.
Under the hood, the anonymous method is implemented by hoisting captured variables (which is what your question body is all about) and storing them as fields of a compiler generated class. There is no way to store a ref or out parameter as a field. Eric Lippert discussed it in a blog entry. Note that there is a difference between captured variables and lambda parameters. You can have "formal parameters" like the following as they are not captured variables:
delegate void TestDelegate (out int x);
static void Main(string[] args)
{
TestDelegate testDel = (out int x) => { x = 10; };
int p;
testDel(out p);
Console.WriteLine(p);
}
You can but you must explicitly define all the types so
(a, b, c, ref d) => {...}
Is invalid, however
(int a, int b, int c, ref int d) => {...}
Is valid
As this is one of the top results for "C# lambda ref" on Google; I feel I need to expand on the above answers. The older (C# 2.0) anonymous delegate syntax works and it does support more complex signatures (as well closures). Lambda's and anonymous delegates at the very least have shared perceived implementation in the compiler backend (if they are not identical) - and most importantly, they support closures.
What I was trying to do when I did the search, to demonstrate the syntax:
public static ScanOperation<TToken> CreateScanOperation(
PrattTokenDefinition<TNode, TToken, TParser, TSelf> tokenDefinition)
{
var oldScanOperation = tokenDefinition.ScanOperation; // Closures still work.
return delegate(string text, ref int position, ref PositionInformation currentPosition)
{
var token = oldScanOperation(text, ref position, ref currentPosition);
if (token == null)
return null;
if (tokenDefinition.LeftDenotation != null)
token._led = tokenDefinition.LeftDenotation(token);
if (tokenDefinition.NullDenotation != null)
token._nud = tokenDefinition.NullDenotation(token);
token.Identifier = tokenDefinition.Identifier;
token.LeftBindingPower = tokenDefinition.LeftBindingPower;
token.OnInitialize();
return token;
};
}
Just keep in mind that Lambdas are procedurally and mathematically safer (because of the ref value promotion mentioned earlier): you might open a can of worms. Think carefully when using this syntax.
And maybe this?
private void Foo()
{
int value;
Bar(out value);
}
private void Bar(out int value)
{
value = 3;
int[] array = { 1, 2, 3, 4, 5 };
var val = value;
int newValue = array.Where(a => a == val).First();
}
You can not use an out parameter directly in a lambda expression. The reason why you can not do that is explained in the other answers.
Workaround
But you can use a local temporary variable with for the inner function and, after the inner function has been executed, assign the out value from the inner function to the out value of the outer function:
private static int OuterFunc (int i_param1, out int o_param2)
{
int param2 = 0;
var del = () => InnerFunc (i_param1, out param2);
int result = del ();
o_param2 = param2;
return result;
}
private static int InnerFunc (int i_param1, out int o_param2)
{
o_param2 = i_param1;
return i_param1;
}
private static void Main (string[] args)
{
int result = OuterFunc (123, out int param2);
Console.WriteLine (result); // prints '123'
Console.WriteLine (param2); // prints '123'
}
Please note
The question was created in 2009. My answer was created in 2023 using C#10 and .NET 6. I don't know whether this answer had also worked back in 2009, which means, the code here might depend on enhancements to C# and .NET that might have been made in the meantime.
I will give you another example.
Description
The code below will throw out this error. Because the change brought by the lambda expression (i)=>{...} only works in the function test.
static void test(out System.Drawing.Image[] bitmaps)
{
int count = 10;
bitmaps = new System.Drawing.Image[count];
Parallel.For(0, count, (i) =>
{
bitmaps[i] = System.Drawing.Image.FromFile("2.bmp");
});
}
Solution
So, if you remove out of the parameter, it works.
static void test(System.Drawing.Image[] bitmaps)
{
int count = 10;
bitmaps = new System.Drawing.Image[count];
Parallel.For(0, count, (i) =>
{
bitmaps[i] = System.Drawing.Image.FromFile("2.bmp");
});
}
If you need out really, don't change the parameter in the lambda expression directly. Instead, use a temporary variable please.
static void test(out System.Drawing.Image[] bitmaps)
{
int count = 10;
System.Drawing.Image[] bitmapsTemp = new System.Drawing.Image[count];
Parallel.For(0, count, (i) =>
{
bitmapsTemp[i] = System.Drawing.Image.FromFile("2.bmp");
});
bitmaps = bitmapsTemp;
}