Mock testing means unit testing with mock objects as substitutes for real objects. By real objects I mean the objects the tested unit (class) will be using in the real application. If you have a class Calculator, that needs a dao (Data Access Object) object to load the data it needs from a database, then the dao object is a "real object". In order to test the Calculator class you will have to provide it with a dao object that has a valid connection to the database. In addition you have to insert the data needed for the test into the database.html
Setting up the connection and inserting the data in the database can be a lot of work. Instead you can provide the Calculator instance with a fake dao class which just returns the data you need for the test. The fake dao class will not actually read the data from the database. The fake dao class is a mock object. A replacement for a real object which makes it easier to test the Calculator class. Purist mock testers would call such a fake dao for a stub. I will get to that distinction later.java
mock測試意味着使用真實對象的替代品mock對象來進行單元測試。真實對象就是指的會在真實項目中使用的測試對象。若是你有一個calculator類,須要一個dao對象來從數據庫加載數據。那麼這個時候dao對象就是一個「真實的對象」。爲了測試calculator類,你須要提供一個和數據庫有有效聯繫的dao對象。並且你必須向數據庫插入須要測試用的數據。數據庫
配置同數據庫間的聯繫和插入數據可能須要大量的工做。那麼你就能夠爲calculator實例提供一個假的dao類用來僅僅返回你須要的數據就能夠了。這個假的dao類並不會真的去讀取數據庫的數據。這個假的dao類就是一個nock對象。用來替換真實的對象使得更容易測試calculator類。express
我的理解: mock就是對象的複製品。stub能夠負責指定須要測試的數據。app
The situation of the Calculator using a dao object can be generalized to "a unit that uses a collaborator". The unit is the Calculator and the collaborator is the dao object. I'll express it like this:ide
unit --> collaborator
The arrow means "uses". When the collaborator is exchanged with a mock (or stub), it would be expressed like this:單元測試
unit --> mock
In a unit test situation it will look like this:測試
unit test --> unit --> collaborator
... or...flex
unit test --> unit --> mock
Stubs, Mocks, and Proxies
There are three types of fake objects you can use for testing: Stubs, Mocks and Proxies. Remember, a stub, mock, or proxy replaces a collaborator of the tested unit during unit test. The stubs and mocks follows Martin Fowlers definition of stubs and mocks.ui
A Stub is an object that implements an interface of a component, but instead of returning what the component would return when called, the stub can be configured to return a value that suits the test. Using stubs a unit test can test if a unit can handle various return values from its collaborator. Using a stub instead of a real collaborator in a unit test could be expressed like this:
stub是用來執行組件接口的對象,可是並非調用組件應當返回的值,stub能夠爲了單元測試而被配置返回值。使用stubs單元測試就能夠處理多種不一樣的返回值狀況用來測試了。
- unit test --> stub
- unit test --> unit --> stub
- unit test asserts on results and state of unit
First the unit test creates the stub and configures its return values. Then the unit test creates the unit and sets the stub on it. Now the unit test calls the unit which in turn calls the stub. Finally the unit test makes assertions about the results of the method calls on the unit.
A Mock is like a stub, only it also has methods that make it possible determine what methods where called on the Mock. Using a mock it is thus possible to both test if the unit can handle various return values correctly, and also if the unit uses the collaborator correctly. For instance, you cannot see by the value returned from a dao object whether the data was read from the database using a Statement or a PreparedStatement. Nor can you see if the connection.close() method was called before returning the value. This is possible with mocks. In other words, mocks makes it possible to test a units complete interaction with a collaborator. Not just the collaborator methods that return values used by the unit. Using a mock in a unit test could be expressed like this:
首先單元測試建立stub而且配置他的返回值。而後單元測試建立單元而且設置stub。如今單元測試調用單元就是調用stub。最後單元測試使用assertions來處理單元的方法調用結果。
mock相似stub,只是他也有方法能夠來肯定mock調用什麼方法和何時調用。若是unit能夠正確處理多個返回值,那麼使用mock就能夠都測試到這些結果。能夠用來測試交互行爲。
- unit test --> mock
- unit test --> unit --> mock
- unit test asserts on result and state of unit
- unit test asserts on the methods called on mock
First the unit test creates the mock and configures its return values. Then the unit test creates the unit and sets the mock on it. Now the unit test calls the unit which in turn calls the mock. Finally the unit test makes assertions about the results of the method calls on the unit. The unit test also makes assertions on what mehods were called on the mock.
首先unit test建立mock而且配置他的返回值。而後單元測試建立單元而且爲他們設置mock。如今單元測試調用unit,unit調用mock。最後使用assertion來判斷結果。
Proxies in mock testing are mock objects that delegate the method calls to real collaborator objects, but still records internally what methods were called on the proxy. Thus proxies makes it possible to do mock testing with real collaborators. Using a proxy in a unit test could be expressed like this:
- unit test --> collaborator
- unit test --> proxy
- unit test --> unit --> proxy --> collaborator
- unit test asserts on result and state of unit
- unit test asserts on methods called on proxy
First the unit test creates the collaborator. Then the unit test creates the proxy for the collaborator. Third the unit test creates the unit and sets the proxy on it. Now the unit test calls the unit which in turn calls the proxy. Finally the unit test makes assertions about the results of the method calls on the unit. The unit test also makes assertions on what mehods were called on the proxy.
Stub, Mock, and Proxy Testing with Testimonial
There are several popular mock testing API's available. Among others JMock and EasyMock. As of writing these two API's do not support proxies as described above. Note: They *use* java.lang.reflect.Proxy instances to implement their dynamic mocks. But that is not the same as the test proxies described above. These API's can only be used with stubs and mocks, not proxy for real collaborators. I'm sure they'll add it in the future.
For the code examples in this text I will be using my own testing API, Butterfly Testing Tools . I developed Butterfly Testing Tools because I needed the proxy testing feature that neither JMock nor EasyMock has. In addition the API's could be designed a bit more straigtforward and flexible, in my opinion (that, I guess, is a matter of personal style).
First lets see how to create stub for an interface:
Connection connection = (Connection) MockFactory.createProxy(Connection.class);
The connection variable is a stub of the Connection interface. I can now call methods on the connection instance just as if it was a real database connection. The methods will only return null though, since the stub is not configured to return any special value. In order to configure the stub to return the values appropriate for your test, you must obtain the Mock for the stub. Here is how that is done:
IMock mock = MockFactory.getMock(connection);
Now you have the mock associated with the stub. One of the methods the IMock interface has is
addReturnValue(Object returnValue);
Using the addReturnValue method you can add return values to the stub. You can add as many as you want. The return values will be returned from the stub in the same sequence they were added. Once a return value has been returned it is removed from the stub, just like in a queue. Note: The sequence of added return values must match the sequence of called methods on the stub! If you add a String "myReturnValue" as return value to the stub and then call connection.prepareStatement("select * from houses") which returns a PreparedStatement, you will get an exception. The String return value cannot be returned from the connection.prepareStatement("..."); You will have to make sure yourself that the return values and called methods on the stub match.
Once you have the IMock instance for a stub you can also make assumptions about what methods were called on the stub. The IMock interface has a series of assertCalled(MethodInvocation) methods for this purpose. Here is an example:
mock.assertCalled(new MethodInvocation("close"));
If the connection.close() method has not been called a java.lang.AssertionError is thrown. If you are using JUnit for your unit test JUnit will catch the AssertionError and report that the test failed. There are other assertCalled() etc. methods on the IMock interface. See the Testimonial project for more info.
The last thing I will show is how to make a proxy for a real connection and get the mock associated with the proxy:
//opens a real database connection.
Connection realConnection = getConnection(); Connection proxyConnection = MockFactory.createProxy(realConnection);
IMock mock = MockFactory.getMock(proxyConnection);
Simple, isn't it? It's just the same as when creating stubs for interfaces. You just provide the real collaborator to the MockFactory instead of an interface (class object). The proxyConnection will record all methods called on it before forwarding the method call to the realConnection instance. That way you can use the proxyConnection just as fine as a real connection, and at the same time make mock assertions about what methods were called on it.
You can even turn the proxyConnection into a stub temporarily by adding a return value to the proxy via the mock.addReturnValue(...). When the proxyConnection sees a return value it will return that instead of forwarding the call to the realConnection. Once all return values have been returned the proxyConnection will continue forwarding the method calls to the realConnection instance. That way you can switch between using the proxyConnection as a real connection or a stub. Smart, isn't?
It is not just database connections that are useful to mock. Any collaborator of a tested unit can potentially be mocked during testing. Whether to test a unit using a mock or a real collaborator depends on the situation. Proxies make it possible to do both at the same time, and even stub some method calls along the way. For more information about mock testing with Testimonial, see the Testimonial project page.