Hi guys,
I need to test the handler of a Timer in one class. I'm using Future Mocking to mock the Timer inside it.
Is there some way in order to invoke the callback?
Here my class to test:
internal
class
DatabaseCleaner
{
private
Timer mobjTimer =
null
;
internal
void
Start()
{
mobjTimer =
new
Timer(CleanDB,
null
, 5000, 1000 * 60 * 10);
}
internal
void
Stop()
{
if
(mobjTimer !=
null
)
{
mobjTimer.Dispose();
mobjTimer =
null
;
}
}
private
void
CleanDB(
object
state)
{
// do stuff to test
}
}
Thanks a lot.
Stefano
4 Answers, 1 is accepted
0
Hi Stefano,
Yes, you can invoke the private method of an internal class. As you haven't mentioned, I will assume that the internal class DatabaseCleaner is defined in a different assembly than where the test is defined. If this is the case, then what you should do is obtain an instance of that internal class.
Here is an example of how this could be done:
After that, you should create a PrivateAccessor with an argument the created instance and call the required method. Here is an example:
If the test scenario requires it you could arrange the call to CleanDB to executed just once or the appropriate amount. Here an example of how a full test for this method could look like:
I hope that this information answers your question.
Regards,
Mihail
Progress Telerik
Yes, you can invoke the private method of an internal class. As you haven't mentioned, I will assume that the internal class DatabaseCleaner is defined in a different assembly than where the test is defined. If this is the case, then what you should do is obtain an instance of that internal class.
Here is an example of how this could be done:
var typeInfo = Assembly.Load(
"ClassLibrary"
).DefinedTypes.Where((t) => t.FullName ==
"ClassLibrary.DatabaseCleaner"
).FirstOrDefault();
var type = typeInfo.AsType();
var instance = Activator.CreateInstance(type);
After that, you should create a PrivateAccessor with an argument the created instance and call the required method. Here is an example:
object
state = 10;
PrivateAccessor privateAccessor =
new
PrivateAccessor(instance);
privateAccessor.CallMethod(
"CleanDB"
, state);
If the test scenario requires it you could arrange the call to CleanDB to executed just once or the appropriate amount. Here an example of how a full test for this method could look like:
[TestMethod]
public
void
TestMethod()
{
// Arrange
var typeInfo = Assembly.Load(
"ClassLibrary"
).DefinedTypes.Where((t) => t.FullName ==
"ClassLibrary.DatabaseCleaner"
).FirstOrDefault();
var type = typeInfo.AsType();
var instance = Activator.CreateInstance(type);
object
state = 10;
Mock.NonPublic.Arrange(type,
"CleanDB"
, state).CallOriginal().OccursOnce();
// Act
Mock.NonPublic.MakePrivateAccessor(instance).CallMethod(
"CleanDB"
, state);
// Assert
Mock.Assert(instance);
}
I hope that this information answers your question.
Regards,
Mihail
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0

Stefano
Top achievements
Rank 1
answered on 25 Feb 2019, 01:41 PM
Hi Mihail,
Thanks for the reply.
The assembly of the test is the same of my class. Sorry, I forgot to tell you.
I saw your solution, but is there come way to mock the timer? Because in your way I'm not testing the timer feature.. but I'm just testing a private method.
Thanks,
Stefano
0
Hi Stefano,
It seems I didn't understand your question correctly. Please excuse me.
You should use future mocking for the timer. However, I am afraid that your exact scenario with the private callback CleanDB is not supported. I have logged a new feature request of your behalf. Here is a link if you would like to follow the item and receive status updates: Future mocking of public class with non-public arguments
I am not sure if this is an option for you but I will share it anyway. If you modify the access modifier of the callback CleanDB to "internal" then the test case will be possible. The reason behind this is that the future mocking depends on expressions and in order for the expression to be compiled successfully all elements of that expression should be visible to the test method.
Here is how the test method will look like in this case:
Please have in mind that in this test I am asserting that the callback is called by the timer. I am also triggering the execution of the callback faster than the original code. I am doing this to speed up the test and it is based on the assumption that the Timer will work as expected regardless of the exact arguments for triggering the callback.
If you would like to assert that the CleanDB is executed then you should find a way to verify that depending on the job done inside that method. Here is how the arrangement should look like in this case:
I hope this helps.
Regards,
Mihail
Progress Telerik
It seems I didn't understand your question correctly. Please excuse me.
You should use future mocking for the timer. However, I am afraid that your exact scenario with the private callback CleanDB is not supported. I have logged a new feature request of your behalf. Here is a link if you would like to follow the item and receive status updates: Future mocking of public class with non-public arguments
I am not sure if this is an option for you but I will share it anyway. If you modify the access modifier of the callback CleanDB to "internal" then the test case will be possible. The reason behind this is that the future mocking depends on expressions and in order for the expression to be compiled successfully all elements of that expression should be visible to the test method.
Here is how the test method will look like in this case:
[TestMethod]
public
void
TestMethod()
{
bool
isProcessed =
false
;
// Arrange
var dbCleaner = Mock.Create<DatabaseCleaner>(Behavior.CallOriginal);
Mock.Arrange(() =>
new
Timer(dbCleaner.CleanDB,
null
, 5000, 1000 * 60 * 10))
.DoInstead(() =>
new
Timer((o)=> isProcessed =
true
,
null
, 0, 0));
// Act
dbCleaner.Start();
Thread.Sleep(1000);
// Assert
Assert.AreEqual(
true
, isProcessed);
}
Please have in mind that in this test I am asserting that the callback is called by the timer. I am also triggering the execution of the callback faster than the original code. I am doing this to speed up the test and it is based on the assumption that the Timer will work as expected regardless of the exact arguments for triggering the callback.
If you would like to assert that the CleanDB is executed then you should find a way to verify that depending on the job done inside that method. Here is how the arrangement should look like in this case:
Mock.Arrange(() =>
new
Timer(dbCleaner.CleanDB,
null
, 5000, 1000 * 60 * 10))
.DoInstead(() =>
new
Timer(dbCleaner.CleanDB,
null
, 0, 0));
I hope this helps.
Regards,
Mihail
Progress Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Hello Stefano,
I hope you are doing well. I would like to let you know about the available options we have found which can solve the issue using the current JustMock API level.
The first option verifies the exact call to the private will be made. In this case you can create delegate and bind the instance method DatabaseCleaner.CleanDB to it. Than the result delegate can be used as an argument matcher in the arrangement. Here is the code:
[TestMethod]
public void TestMethod2()
{
// Arrange
var sut = Mock.Create<
DatabaseCleaner
>(Behavior.CallOriginal);
var dbCleanerTimerCallback = (TimerCallback)Delegate.CreateDelegate(typeof(TimerCallback), sut, "CleanDB");
Mock.Arrange(() => new Timer(dbCleanerTimerCallback, null, 5000, 1000 * 60 * 10))
.IgnoreInstance()
.MustBeCalled();
// Act
var dbCleaner = new DatabaseCleaner();
dbCleaner.Start();
// Assert
Mock.Assert(sut);
}
The second (more generic) option verifies that the delegate of a given type will be called. As an opposite for the previous option, here the argument matcher looks for the specified type, but not for the particular instance. The test looks like as following:
[TestMethod]
public void TestMethod3()
{
// Arrange
var sut = Mock.Create<
DatabaseCleaner
>(Behavior.CallOriginal);
Mock.Arrange(() => new Timer(Arg.IsAny<
TimerCallback
>(), Arg.AnyObject, Arg.AnyInt, Arg.AnyInt))
.IgnoreInstance()
.MustBeCalled();
// Act
var dbCleaner = new DatabaseCleaner();
dbCleaner.Start();
// Assert
Mock.Assert(sut);
}
It would be nice to share whether these solutions are good enough for you, so we can plan further activities on the related feature request.
Regards,
Ivo
Progress Telerik
Do you want to have your say when we set our development plans?
Do you want to know when a feature you care about is added or when a bug fixed?
Explore the
Telerik Feedback Portal
and vote to affect the priority of the items