The importance of unit testing does not need to be redundant, but unit testing can also be difficult, one of which is how to create dependencies. Imagine our common server-side tiered architecture, the data Access layer DAO, the business layer, and the web layer, and want the unit test business layer, we need to rely on the DAO layer to provide data support, the DAO layer is dependent on the database, the database needs Shema and data. In order to test the business logic, it is necessary to prepare so many things, think about the trouble, unit test enthusiasm also minus half. The solution to this problem is the mock technique, which simulates the behavior of the component being relied on. Let's talk about unit tests .
Unit test is simply the test of class, the test object is a class, the test content is the correctness of class logic, here to emphasize that the test content only focus on the logic of the test class itself, the class of the test depends on the logic of the class, should be the other unit test to cover. The following figure is a simple schematic:
Unit tests only focus on the logic of target. If there is no target Dependency, then of course better, direct testing. What if there is target dependency? Sometimes it's easy to build target dependency, new object. What about the DAO dependency class to the beginning? It's OK to create a database, initialize the table structure and data, but these are not related to the correctness of the business layer we want to test.
A more eco-friendly approach is to mock DAO classes. But how to mock. Mock what content. There are many good mock tools in the Java world, such as Easymock, Jmock, and Mockito, which can be used to simplify our mock work. This article only discusses Mockito. As for the question of what to mock, my understanding is the behavior of mock classes, that is, the Method,value object of the class is created directly. The method of a class is roughly divided into the following: Method name method input parameter method output return value method Exception method EXECUTE statement
The name of method certainly cannot change, there is no mock argument. Everything else can be mock. Dao Mock Example
Prepare entity and DAO interfaces first:
public class User {
private Long ID;
private String name;
Public Long GetId () {return
ID;
}
public void SetId (Long id) {
this.id = ID;
}
Public String GetName () {return
name;
}
public void SetName (String name) {
this.name = name;
}
}
Public interface Userdao {
/**
* Add user
/
void Insertuser /**
* Inquiry User * *
queryuser (Long ID);
Here is the business service and implementation, TDD is the practice of first unittest after the implementation of the service, the concern is not
It's TDD, it's a mock, and it's not going to take a moment.
Public interface UserService {/** * Create new user */void CreateNewUser (user user) throws Excep
tion;
public class Userserviceimpl implements UserService {private Userdao Userdao; public void CreateNewUser (user user) throws Exception {//Parameter check if (user = null | | User.getid () = NULL | |
IsEmpty (User.getname ()) {throw new illegalargumentexception ();
//See if duplicate data Long id = User.getid ();
User Dbuser = userdao.queryuser (ID);
if (dbuser!= null) {throw new Exception ("User already exists");
try {userdao.insertuser (dbuser);
catch (Exception e) {//Hide database exception, throw service exception throw new Exception ("Database statement execution failed", e); } Private Boolean IsEmpty (String str) {if (str = NULL | | Str.trim (). Length () = 0) {Retu
RN true;
return false; public void Setuserdao (useRdao Userdao) {This.userdao = Userdao;
}
}
Start Unit test:
Import Org.junit.Test;
Import Org.mockito.invocation.InvocationOnMock;
Import Org.mockito.stubbing.Answer;
Import static org.mockito.mockito.*;
Import java.sql.SQLException; public class Userservicetest {@Test (expected = illegalargumentexception.class) public void Testnulluser () throws
Exception {UserService userservice = new Userserviceimpl ();
Create mock Userdao Userdao = mock (userdao.class);
((Userserviceimpl) userservice). Setuserdao (Userdao);
Userservice.createnewuser (NULL); @Test (expected = illegalargumentexception.class) public void Testnulluserid () throws Exception {Userse
Rvice userservice = new Userserviceimpl ();
Create mock Userdao Userdao = mock (userdao.class);
((Userserviceimpl) userservice). Setuserdao (Userdao);
User user = new user ();
User.setid (NULL);
Userservice.createnewuser (user); } @Test (expected = Illegalargumentexception.class) PUBlic void Testnullusername () throws Exception {UserService userservice = new Userserviceimpl ();
Create mock Userdao Userdao = mock (userdao.class);
((Userserviceimpl) userservice). Setuserdao (Userdao);
User user = new user ();
User.setid (1L);
User.setname ("");
Userservice.createnewuser (user); @Test (expected = exception.class) public void Testcreateexistuser () throws Exception {UserService user
Service = new Userserviceimpl ();
Create mock Userdao Userdao = mock (userdao.class);
User Returnuser = new user ();
Returnuser.setid (1L);
Returnuser.setname ("Vikey");
When (Userdao.queryuser (1L)). Thenreturn (Returnuser);
((Userserviceimpl) userservice). Setuserdao (Userdao);
User user = new user ();
User.setid (1L);
User.setname ("Vikey");
Userservice.createnewuser (user); @Test (expected = exception.class) public void tesTcreateuserondatabaseexception () throws Exception {UserService userservice = new Userserviceimpl ();
Create mock Userdao Userdao = mock (userdao.class);
Dothrow (New SQLException ("SQL isn't valid"). When (Userdao). Insertuser (Any (user.class));
((Userserviceimpl) userservice). Setuserdao (Userdao);
User user = new user ();
User.setid (1L);
User.setname ("Vikey");
Userservice.createnewuser (user);
@Test public void Testcreateuser () throws Exception {UserService userservice = new Userserviceimpl ();
Create mock Userdao Userdao = mock (userdao.class);
Doanswer (New answer<void> () {public Void Answer (Invocationonmock invocation) throws Throwable {
System.out.println ("Insert Data into User table");
return null;
. When (Userdao). Insertuser (Any (user.class)); ((Userserviceimpl) userservice). Setuserdao (Userdao);
User user = new user ();
User.setid (1L);
User.setname ("Vikey");
Userservice.createnewuser (user);
}
}
The first three test cases were tested for the service's handling of illegal input parameters, with no Userdao, no mocks. The 4th Test case test is the processing logic of the service, UserService how to deal with the new existing user problem, we expect the error. To determine whether the user exists, need to use the Userdao.queryuser interface, so we mock the Queryuser method. Returns a user object when the parameter is 1L. The 5th test case tests the case of a DAO throw exception. The 6th test case tests the normal processing situation, Doanswer only shows the use of no return value method, which can not be written. Conclusion
Mockito is powerful and has the function of memorizing method calls, but I don't think unittest should focus too much on mocks, but target. Mockito syntax is very strange, and our usual contact with the Java code is not, familiar with it will find its API very beautiful. Reference Links The classical-Mockito design analysis of the anti-pattern of the design idea behind the Mockito author's explanation