This article is the second in a series of Productive Java with Ruby articles. Through the introduction of the previous article, I think you have a basic understanding of how to use Ruby for unit testing. Advanced topics when unit testing.
In general, the introduction of new technologies can only reduce the difficulty of solving problems, not eliminate the problems themselves!
Struggling in the primitive jungle of "dependence" ...
With Ruby, we can deal with data preparation more efficiently, but the real world is not that simple! With the deepening of the test, we will feel more and more struggling in the primitive jungle of "dependence"! Sometimes it seems that you need to add countless jar packages, initialize all the components, and configure all the relationships between the database, server, and network before you can start a short test. What is even more painful is that all of this is so fragile, just that someone has added a piece of data to the database or changed a part of the environment configuration, and all the tests you have painstakingly built will strike! How many times have you sighed: "God! Save me ...". But where is God?
Mock
The unit test is effective because we follow the principle of fast feedback and small steps! Only test one thing at a time! The reliance on a lot of resolving work obviously makes the unit test deviate from the original goal, which is also uncomfortable. Mock technology can effectively get rid of the nightmare in the jungle. We know that in the world of computers, the same input can certainly get the corresponding output, otherwise it is an abnormal situation. Mock technology is essentially to get rid of the dependence on the realization of the program by intercepting and replacing the return value of the specified method. For the calculation of input conditions such as 1 + 1, the Mock technology directly intercepts the original method and replaces the calculation method with a return value of 2, regardless of whether the algorithm is obtained through the network or through local calculations. In this way, it is decoupled from the concrete realization.
When unit testing Java, it usually depends on a specific class or an interface. To decouple, you need to be able to mock the specific class or interface. Fortunately, these are very simple in JRuby, because JtestR automatically introduced the mocha Mock framework for us, so that we can start working more easily. First look at a Mock test for HashMap:
map = mock (HashMap) # => mock java.util.HashMap class, if it is an interface, it can be directly new, such as Map.new
map.expects (: size) .returns (5) # => Simulate and expect to return 5 when calling the size method
assert_equal 5, map.size # => assert, very similar to JUnit assert
EasyMock is a popular open source Java Mock testing framework. There are examples of how to use Mock for testing in its official website documentation. For convenience, I will directly quote this example and use JRuby to implement Mock-based testing. First we have an interface:
// Collaborator interface to track the relevant status of the collaboration document
public interface Collaborator {
Void documentAdded (String title); // triggered when a document is added
Void documentChanged (String title); // triggered when the document changes
Void documentRemoved (String title); // triggered when the document is deleted
Byte voteForRemoval (String title); // When the document is shared and the delete operation is performed, the voting action is performed
Byte [] voteForRemovals (String [] title); // Same as above, but you can vote for multiple documents at the same time
}
In this example, there is also a ClassUnderTest class that implements related logic for managing collaborative documents. The simplified sample code is as follows:
public class ClassUnderTest {
// ...
Public void addListener (Collaborator listener) {
// Add collaborators
}
Public void addDocument (String title, byte [] document) {
// ...
}
Public boolean removeDocument (String title) {
// ...
}
Public boolean removeDocuments (String [] titles) {
// ...
}
}