The JustMock R3 release is now live, bringing support for C# 7 features "ref return values and ref locals," "local functions" as well as "named parameters" and mocking of non-public property setters.
If you have struggled with how to test a method that returns a reference, or the local function defined in a method, then this is the blog post you should read. Read on to see everything that's new in the Telerik JustMock R3 2018 release.
Ref Return Values and Ref Locals
If you have followed the development of C# language you couldn’t miss the introduction of “ref return values and ref locals.” Testing this feature in the code is often related to the word struggle. The R3 2018 release makes the testing as easy as writing several lines of code. Consider the following methods that should be tested:
internal
class
Foo
{
private
static
int
[] array = { 1, 2, 3, 4, 5, 6 };
public
ref
int
GetRefReturnInstanceWithArgs(
ref
int
p)
{
ref
int
local =
ref
array[0];
local += p;
return
ref
local;
}
private
static
ref
int
GetRefReturnPrivateStaticWithArgs(
ref
int
p)
{
ref
int
local =
ref
array[0];
local += p;
return
ref
local;
}
}
One of the intriguing parts of testing this code is how a value considered ref return can be created and returned instead of the original value. JustMock introduces a dedicated delegate for handling such a scenario and a helper class LocalRef which simplifies its usage. Check the following code to see a test example of how the ref return value is created and used:
[TestMethod]
public
void
MockRefReturnInstanceMethodWithArgs()
{
// Arrange
int
expectedValue = 12;
var sut = Mock.Create<Foo>();
LocalRefHandle<
int
> localRef = LocalRef.WithValue(expectedValue);
Mock.Arrange(sut, s => s.GetRefReturnInstanceWithArgs(
ref
Arg.Ref(Arg.AnyInt).Value))
.Returns(localRef.Handle)
.OccursOnce();
// Act
int
param = 10;
ref
int
result =
ref
sut.GetRefReturnInstanceWithArgs(
ref
param);
// Assert
Mock.Assert(sut);
Assert.Equal(expectedValue, result);
}
And what about when the method is private or even private static? It should be a lot harder to test it, right? Not at all.
We are following the already known approach of mocking nonpublic API with the Mock.NonPublic expectation. But for the sake of avoiding confusion about which exact arrange method in the nonpublic expectation should be used, we extracted the required logic in a separate interface named INonPublicRefReturnExpectation.
Check the following test example:
[TestMethod]
public
void
MockRefReturnPrivateStaticMethodWithArgs()
{
// Arrange
int
expectedValue = 12;
Mock.SetupStatic<Foo>();
LocalRefHandle<
int
> localRef = LocalRef.WithValue(expectedValue);
Mock.NonPublic.RefReturn.Arrange<Foo,
int
>(
"GetRefReturnPrivateStaticWithArgs"
, ArgExpr.Ref(1))
.Returns(localRef.Handle)
.OccursOnce();
// Act
var privateAccessor = Mock.NonPublic.MakeStaticPrivateAccessor(
typeof
(Foo));
int
param = 10;
ref
int
res =
ref
privateAccessor.RefReturn.CallMethod<
int
>(
"GetRefReturnPrivateStaticWithArgs"
, Arg.Ref(param).Value);
// Assert
Mock.Assert<Foo>();
Assert.Equal(expectedValue, res);
}
As I said, several lines of code for a test following the Arrange, Act, Assert (AAA) testing pattern and the struggle is over. If you need more information you could check out our documentation article on the subject.
Local Functions
Local Functions is another feature of the C# language that developers get excited to use when they first hear about it. But then they take a step back after stumbling over the question of "how to test a local function?" The reason is simple. Until now there was no effective and maintainable way of testing local functions.
Telerik JustMock is now the first tool that provides support for testing and mocking local functions in reliable manner. Now to the more practical part. How exactly I can write my tests? Consider the following class that should be tested:
internal
class
Foo
{
public
int
GetResult()
{
return
100 + GetLocal();
int
GetLocal()
{
return
42;
}
}
}
To mock this simple local function, I would need information about the name of the class, the name of the owner method and the name of the local function. Here is what the test would look like:
[TestMethod]
public
void
TestBasicScenario()
{
//Arrange
var sut = Mock.Create<Foo>(Behavior.CallOriginal);
Mock.Local.Function.Arrange<
int
>(sut,
"GetResult"
,
"GetLocal"
).DoNothing();
//Act
var result = sut.GetResult();
//Assert
Assert.AreEqual(100, result);
}
You can see that the mock is created with the call original behavior. This is required for the owner method GetResult to execute the original code.
This was the most basic case, but what about if an overload that takes parameters and has a local function as well is added later to the code? Consider the following overload:
public
int
GetResult(
int
param)
{
return
param + 100 + GetLocal();
int
GetLocal()
{
return
200;
}
}
Well in this case another test should be written for the second overload with providing information about the types of its parameters. Here is what the test would look like:
[TestMethod]
public
void
MockGetResultParams()
{
//Arrange
var sut = Mock.Create<Foo>(Behavior.CallOriginal);
Type[] containingMethodParamTypes =
new
Type[] {
typeof
(
int
) };
Mock.Local.Function.Arrange<
int
>(sut,
"GetResult"
, containingMethodParamTypes,
"GetLocal"
).Returns(42);
//Act
var resultParams = sut.GetResult(10);
//Assert
Assert.AreEqual(42, resultParams);
}
JustMock now supports also directly executing a local function. Simply use the Call method in the IFunctionExpectation interface. Consider the following class that should be tested:
internal
class
Foo
{
private
bool
IsEven(
int
value)
{
bool
isEven = (value % 2 == 0)?
true
:
false
;
return
isEven;
}
public
void
Method()
{
bool
result = LocalFunction(10);
bool
LocalFunction(
int
value)
{
return
IsEven(value);
}
}
}
And here is what the test would look like:
[TestMethod]
public
void
CallLocal()
{
//Arrange
var sut = Mock.Create<Foo>(Behavior.CallOriginal);
Mock.NonPublic.Arrange<
bool
>(sut,
"IsEven"
).Returns(
false
);
//Act
var result = Mock.Local.Function.Call(sut,
"Method"
,
"LocalFunction"
, 14);
//Assert
Assert.Equal(
false
, result);
}
If you're wondering, JustMock supports also local functions used in private and static methods and classes. For more information you could check our documentation article.
Mocking Non-Public Property Setters
If you have used some unpleasant workaround to mock a non-public property setter than I have good news for you. You can safely delete it. We have introduced the ArrangeSet method in the INonPublicExpectation interface accessible through the Mock class to help you test those private setters. Just take a look at the following test example to get a better understanding of the concept.
Here is the private property that would be tested:
public
class
Foo
{
private
int
Index {
get
;
set
; }
}
And here is what the test should look like:
[TestMethod]
public
void
MockNonPublicPropArrangeSet()
{
// Arrange
int
expectedValue = 20;
var sut = Mock.Create<Foo>(Behavior.CallOriginal);
var privateAccessor = Mock.NonPublic.MakePrivateAccessor(sut);
Mock.NonPublic.ArrangeSet(sut,
"Index"
, 12).DoInstead(() => privateAccessor.SetProperty(
"Index"
, expectedValue));
// Act
privateAccessor.SetProperty(
"Index"
, 12);
int
res = (
int
)privateAccessor.GetProperty(
"Index"
);
// Assert
Assert.Equal(expectedValue, res);
}
Named Parameters
Named parameters has been a feature of C# for a long time now and it is quite popular among developers. Therefore, a while back we introduced support for mocking instance methods accepting named parameters. But we hadn't implemented support for mocking static methods accepting named parameters. This is now done, and it is as simple as arranging a normal static method. Check the following test example:
Here is the class and the method that would be tested.
internal
class
Foo
{
public
static
void
StaticAction(
int
param1 = 0,
int
param2 = 0,
int
param3 = 0)
{
throw
new
NotImplementedException();
}
}
And here is what the test should look like:
[TestMethod]
public
void
TestStaticMethodAcceptingNamedParameters()
{
// Arrange
Mock.SetupStatic<Foo>();
Mock.Arrange<Foo>(() => Foo.StaticAction(param2: 2, param1: 3)).OccursOnce();
// Act
Foo.StaticAction(3, 2);
// Assert
Mock.Assert<Foo>();
}
Check Out the Latest Version and Share Your Feedback
Make sure to try the latest version of JustMock and get back to us with your feedback. It is already available for download in your account.
Not a JustMock customer? Feel free to download a free 30-day trial.
Watch the R3 Release Webinars
To see all the latest changes and updates across our new release in action, please join us on the Telerik UI R3 2018 webinar, on October 2, 2018 at 11 a.m.