Mock-objects for TorqueScript
by BlueRaja · 07/30/2008 (2:35 pm) · 3 comments
Completed for this release of TUnit: a mocking framework for TorqueScript!
What is a mocking framework?
When running unit-tests, one typically wants to test just that - a unit (or single class). However, if your class relies on other classes, bugs in those other classes could cause tests for your class to fail. This is where mock-objects come into play - mock-objects allow you to, in essence, create "fake instances" for each of the dependencies of your class, meaning your class can be tested in complete isolation from all other classes. You can define expectations of and give custom behaviours to these objects, which your test-class then uses as though they were the actual objects it expects them to be. Now when a unit-test fails, you can be sure that it's because of a bug in the class you're actually testing!
Mock-objects can also be used if your class relies on another class that has not been written yet, and are helpful for a variety of other reasons.
Using TUnit mocks
To create a mock, use Mock::createMock(ClassName), replacing ClassName with the name of the class you want to mock.
Finally, when all expectations are set up, call
For example, to assert that %target.myMethod(%var); will call %var.add(5) at least twice, you might do the following:
Downloading TUnit mocks
The mocks package is included with the latest release of TUnit, a link to which can be found in my previous post on unit-tests.
FAQ.
Q. Where can I learn more about what mock-objects are and how to use them?
A. Try googling.
Q. Is there a way to specify the order I want methods called in, or to do different things on different calls of the same method (with the same parameters)?
A. Yes and no; no, in that neither of these things are directly supported, the way they are in some other mock-packages. Yes, in that these can both be easily accomplished using a callback (calls()) and some simple logic. There are no plans to add either of these features in the future, as I almost never have to use them, but if I get enough requests I may reconsider.
What is a mocking framework?
When running unit-tests, one typically wants to test just that - a unit (or single class). However, if your class relies on other classes, bugs in those other classes could cause tests for your class to fail. This is where mock-objects come into play - mock-objects allow you to, in essence, create "fake instances" for each of the dependencies of your class, meaning your class can be tested in complete isolation from all other classes. You can define expectations of and give custom behaviours to these objects, which your test-class then uses as though they were the actual objects it expects them to be. Now when a unit-test fails, you can be sure that it's because of a bug in the class you're actually testing!
Mock-objects can also be used if your class relies on another class that has not been written yet, and are helpful for a variety of other reasons.
Using TUnit mocks
To create a mock, use Mock::createMock(ClassName), replacing ClassName with the name of the class you want to mock.
%myMockedObj = Mock::createMock(SimSet);Expectations take the form
%mockedVariable.methodToExpect([i]parameters[/i]).[i]expectationModifiers[/i]expectationModifiers consist of the following:
//Call-count
callOnce(); //This method should be called exactly once
callNone(); //This method should never be called
callAny(); //This method can be called any number of times
callExactly(%times); //This method should be called exactly %times times.
callAtLeast(%times); //Call at least %times times
callAtMost(%times); //Call at most %times times
//Actions
calls(%functionName); //Calls the function with the given name. No parentheses are needed.
// The value returned by the function is also returned when the
// mocked-method is called.
returns(%value); //The method returns the value %value. returns() is ignored if
// calls() is specified
callOriginalMethod(); //Calls the original implementation of the method that is being
// mocked.
//Parameter-matching
ignoreArguments(); //Matches the expectation on any call to the method, regardless
// of parameters
ignore(); //Same as ignoreArguments().callAny() (Expectation modifiers can be chained)Finally, when all expectations are set up, call
Mock::replayAll();Then run the test methods, and all expectations with be verified automatically.
For example, to assert that %target.myMethod(%var); will call %var.add(5) at least twice, you might do the following:
function MyTests::myMethodTest()
{
//Setup %target here or in setup()...
%var = Mock::createMock(SimSet);
%var.add(5).callAtLeast(2);
Mock::replayAll();
%target.myMethod(%var);
}More examples can be found in MockTests.csDownloading TUnit mocks
The mocks package is included with the latest release of TUnit, a link to which can be found in my previous post on unit-tests.
FAQ.
Q. Where can I learn more about what mock-objects are and how to use them?
A. Try googling.
Q. Is there a way to specify the order I want methods called in, or to do different things on different calls of the same method (with the same parameters)?
A. Yes and no; no, in that neither of these things are directly supported, the way they are in some other mock-packages. Yes, in that these can both be easily accomplished using a callback (calls()) and some simple logic. There are no plans to add either of these features in the future, as I almost never have to use them, but if I get enough requests I may reconsider.
#2
I'm trying to figure out the best way to implement this in a real game, using realistic tests.
From what I know about unit testing and mocking, a common approach is to isolate the tests to just the specific, desired class through Dependency Injection. The injected dependencies are mock objects. If one of the dependencies is the View object (using a Model-View-Presenter type of Design Pattern), then all of the tests can be run without any UI actually occurring.
I don't quite see how this can be effectively implemented in TorqueScript. Do you have any examples of a real game using TUnit and the mock objects? That would help a lot.
08/06/2008 (5:42 am)
Very nice. The mock framework looks similar in style to Rhino Mocks for C#. I'm trying to figure out the best way to implement this in a real game, using realistic tests.
From what I know about unit testing and mocking, a common approach is to isolate the tests to just the specific, desired class through Dependency Injection. The injected dependencies are mock objects. If one of the dependencies is the View object (using a Model-View-Presenter type of Design Pattern), then all of the tests can be run without any UI actually occurring.
I don't quite see how this can be effectively implemented in TorqueScript. Do you have any examples of a real game using TUnit and the mock objects? That would help a lot.
#3
As for your other point: yes, separating the view from the logic would definitely be for the best, even when not testing. However, I realize that's probably not always possible, for instance when using engine objects that have their logic and view tightly integrated.
The best option would be to create a partial-mock by mocking some methods and not others. To do this, I would have to create something along the lines of a callOriginalMethod expectation - perhaps I'll do that when I get time.
The other option would be to use the fact that the this pointer can be passed as an argument in TorqueScript and mock the target-object itself! That is, rather than doing this:
If you use this second approach, just make sure to test the "private" methods as well.
PS. Rhino Mocks is my favorite mocking framework, so thank you for the compliment. I also read the Ayende Rahien blog regularly, and love it!
[edit]Eh, I added a callOriginalMethod expectation.
08/07/2008 (11:49 am)
Sorry, don't have any real-world examples yet - I learned TorqueScript before ever learning Torque! I'll get back to you in a few weeks on that.As for your other point: yes, separating the view from the logic would definitely be for the best, even when not testing. However, I realize that's probably not always possible, for instance when using engine objects that have their logic and view tightly integrated.
The best option would be to create a partial-mock by mocking some methods and not others. To do this, I would have to create something along the lines of a callOriginalMethod expectation - perhaps I'll do that when I get time.
The other option would be to use the fact that the this pointer can be passed as an argument in TorqueScript and mock the target-object itself! That is, rather than doing this:
%target.myMethod(%parameter);mock the target and do this:
MyClass::myMethod(%target, %parameter);This has the advantage of mocking methods that were intended to be private as well - meaning your test doesn't even need to rely on the validity of methods within the same class!
If you use this second approach, just make sure to test the "private" methods as well.
PS. Rhino Mocks is my favorite mocking framework, so thank you for the compliment. I also read the Ayende Rahien blog regularly, and love it!
[edit]Eh, I added a callOriginalMethod expectation.

Torque 3D Owner Novack
CyberianSoftware