Rhion. mocks released the latest version 3.0.1 today. This is a mocking framework and the developer is ayende.
Difficulties in testing interface programming
Mock framework is used to simulate the behavior of a specific object without implementing a specific object, that is, without a class instance. This feature is very useful for interface-oriented programming. Because the interface caller can use the interface without specific implementation of the interface, that is, the caller can take action before the interface implementer. Some people may think this is nothing magical. Even if there is no mock, I can use the interface, but I have to ask:
"Can you test the code that calls an interface without interface implementation ?"
"Nullreferenceexception" I believe many people have encountered it. Because the interface cannot define the constructor, it cannot be instantiated. As a result, the code that calls the interface cannot run, that is, it cannot be tested.
---------------------------------------------
What can mocking do?
Mock can be understood from its literal meaning. Its main task is to simulate an instance of a simulated object, these include simulating the call behavior (such as access attributes and call methods) of the instance, simulating the return values of methods or attribute access, and passing parameters of simulation methods and indexes, it can be said that the use of an object instance can be simulated. In this way, we can use it normally as if there is an instance we need to develop and test the caller code.
---------------------------------------------------
Is mock object the same as stub object?
Of course not the same! Stub should know that stub is a simulation of an object. For example, if the caller needs a value, Stub should output a value, if the caller needs to pass a value to stub, define a method in stub to accept this parameter. However, this is essentially different from the mock object:
Although stub is also a simulation, it is essentially a simple implementation of real objects. No matter how simple it is, it is an implementation and it actually exists, it contains the operation code defined by us;
In contrast, a mock object does not exist at all. Even a simple code cannot exist.
Before understanding the differences, you need to understand that they all come up for the same purpose, instead of the dependency, so that the original "integration test" is simplified to "unit test ".
Mock: Uses easymock and other packages to inject "dependency" to the tested code in the program code, and simulates the results returned by function calls in a code-programmable manner.
Stub: write your own code to replace "dependency ". It is a simplified implementation of "dependency.
In fact, Stub should not be used when mock can be used. But sometimes stub must be used. For example, when testing the legacy code, this part of the Code does not support "injection", so the process of "substitution" can only be moved out, stub is used to complete this task.
--------------------------------------
Application scenarios
Taking the website code I am developing as an example, if you are testing the mock object. now there is a requirement that we need to search for a project based on the given search keyword and search range. To achieve this in MVP mode, we define an iview interface:
Public interface iview_searchproject
{
Void attachpresenter (presenter_searchproject presentersearchproject );
Searchrange {Get ;}
String searchkey {Get ;}
String urlbase {Get ;}
Void navigateto (string searchurl );
}
And a presenter:
Public class presenter_searchproject
{
Public presenter_searchproject (iview_searchproject viewsearch)
{
View = viewsearch;
Range = view. range;
Prjnav = new projectsearchnavigator (view. urlbase );
Query = new searchquery ();
}
Public String getdesurl ()
{
Query. withdescription = range. withdescription;
Query. withname = range. withname;
Query. withkey = range. withkey;
Query. searchkey = view. searchkey;
Query. IDS = range. IDS;
Prjnav. Compile (query );
Return prjnav. desturl;
}
Public void search ()
{
View. navigateto (getdesurl ());
}
Private iview_searchproject view;
Private searchrange range;
Private projectsearchnavigator prjnav;
Private searchquery query;
}
Projectsearchnavigator is a help class for page Jump. It constructs the address of the search page based on the search keyword searchkey and querystring passed by view (here Is An ASPX page. The searchquery class is responsible for parsing the request. querystring set. Because the key/value pair is stored in the query, a string of all query conditions needs to be constructed accordingly.
Mocking and Testing
In the end, mocking tries multiple times for testing. Otherwise, it is unnecessary because the objects produced by mocking cannot run as real code. First paste the test code and then explain it. I hope you don't feel too much :)
[Testfixture]
Public class presenter_searchproject_test
{
[Setup]
Public void setup ()
{
Mockrepository = new mockrepository (); // 1
Mockview = mockrepository. createmock <iview_searchproject> (); // 2
}
[Test]
Public void getdesturl ()
{
Searchrange range = new searchrange (true, true, false, String. Empty );
// 3
//
Ct. Call (mockview. Range). Return (range );
// Urlbase
Ct. Call (mockview. urlbase). Return ("Http: // localhost");
// Searchkey
Ct. Call (mockview. searchkey). Return ("searchkey ");
// 4
Mockrepository. replayall ();
// 5
Presenter = new presenter_searchproject (mockview );
String desturlreturned = presenter. getdesurl ();
String desturlexpected ="Http: // localhost/projectpage/projectcontrol. aspx?"
+ "Search = searchkey & name = true & Key = true & Description = false ";
// 6
Assert. areequal (desturlexpected, desturlreturned );
}
Iview_searchproject mockview;
Mockrepository;
Presenter_searchproject presenter;
[Teardown]
Public void testcleanup ()
{
Mockrepository. replayall ();
Mockrepository. verifyall ();
}
}
1. The objects to use mock in the rhion. Mock framework must be generated from the mockrepository object and act as an object factory.
2. This step is to create the mock object we use. The mock class type must be used as a generic parameter.
3. The three lines of code in this step are the real mock part, which correspond to three calls to mockview respectively.
Ct. Call (mockview. Range). Return (range );
Except CT. Call indicates the method we want to call mockview, including attributes.
. Return indicates the value to be returned by the simulated object.
In combination, we want the mockview caller to return a value with a return identifier when calling a method (or attribute) of mockview.
4. do not forget the call of replayall. It can be understood to make the previously set simulated behavior take effect, then we can use this mock object as a real object. I think we can think of it as a CLR that automatically generates code for us and generates an implementation for the mock object.
5. This step is the use of the mock object by the caller.
6. test whether the behavior of the target object is normal.
One area to be noticed
Presenter = new presenter_searchproject (mockview );
For initialization like this, you need to pay attention to the sequence. You must wait until the mockview is simulated, that is, after the replayall call, because the mockview members need to be accessed within the presenter, for example:
Range = view. range;
However, if you do not access the members of the mock object during the initialization of the mock object caller, such initialization is acceptable. Although the mock object has mock members, the mock object has been generated:
Mockview = mockrepository. createmock <iview_searchproject> ();
It's just an empty shell :)