假设有如下两张表
订单表:orders
| 字段 |
类型 |
描绘 |
| orderid(主键) |
Int not null |
订单号 |
| amount |
Float null |
订单金额 |
| createdate |
Datetime |
创建日期 |
订单项表:orderitem
| 字段 |
类型 |
描绘 |
| id(主键) |
Int not null |
订单项编号 |
| productname |
Varchar(255) not null |
产品名称 |
| price |
Float null |
产品单价 |
| order_id |
Int null |
订单号 |
双向一对多及多对一
Order.java
package demo.ejb.examples.domain;
import java.io.Serializable;
import java.sql.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
@Table(name = "Orders")
public class Order implements Serializable {
private static final long serialVersionUID = 2092149343338263183L;
private Integer orderid;
private Float amount;
private Date createdate;
private Set<OrderItem> orderItems = new HashSet<OrderItem>();
@Id
@GeneratedValue
public Integer getOrderid() {
return orderid;
}
public void setOrderid(Integer orderid) {
this.orderid = orderid;
}
public Float getAmount() {
return amount;
}
public void setAmount(Float amount) {
this.amount = amount;
}
@Temporal(value = TemporalType.TIMESTAMP)
public Date getCreatedate() {
return createdate;
}
public void setCreatedate(Date createdate) {
this.createdate = createdate;
}
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@OrderBy(value = "id ASC")
public Set<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(Set<OrderItem> orderItems) {
this.orderItems = orderItems;
}
}
OrderItem.java
package demo.ejb.examples.domain;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "OrderItems")
public class OrderItem implements Serializable {
private static final long serialVersionUID = 4255254187929539251L;
private Integer id;
private String productname;
private Float price;
private Order order;
@Id
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(length = 255, nullable = false)
public String getProductname() {
return productname;
}
public void setProductname(String productname) {
this.productname = productname;
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}
@ManyToOne(cascade = CascadeType.REFRESH, optional = false)
@JoinColumn(name = "order_id")
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
EasyMock.createMock(): Creates a mock object that implements the given interface, order
checking is disabled by default.
EasyMock.createNiceMock() : Creates a mock object that implements the given interface, order checking is disabled by default, and
the mock object will return 0, null or false for unexpected invocations.
EasyMock.createStrickMock() : Creates a mock object that implements the given interface, order
checking is enabled by default.
List 1:测试类 Authenticator.java
package com.rolyer.session;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.log.Log;
import org.jboss.seam.security.Credentials;
import org.jboss.seam.security.Identity;
@Name("authenticator")
public class Authenticator
{
@Logger private Log log;
@In Identity identity;
@In Credentials credentials;
public boolean authenticate()
{
log.info("authenticating {0}", credentials.getUsername());
//write your authentication logic here,
//return true if the authentication was
//successful, false otherwise
if ("admin".equals(credentials.getUsername()))
{
identity.addRole("admin");
return true;
}
return false;
}
}
List 2:测试用例 AuthenticatorTest.java
package com.rolyer.session;
import org.easymock.classextension.EasyMock;
import org.jboss.seam.log.Log;
import org.jboss.seam.security.Credentials;
import org.jboss.seam.security.Identity;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.rolyer.sms.util.TestUtils;
public class AuthenticatorTest {
private Authenticator authenticator;
private Log log;
private Identity identity;
private Credentials credentials;
@BeforeMethod
public void beforeMethod() {
authenticator = new Authenticator();
//创建模仿对象的实例
log = EasyMock.createMock(Log.class);
identity = EasyMock.createMock(Identity.class);
credentials = EasyMock.createMock(Credentials.class);
}
@AfterMethod
public void afterMethod() {
authenticator = null;
log = null;
identity = null;
credentials = null;
}
@Test
public void testAuthenticateWithNotAdmin() throws Exception {
//创建模仿对象的实例
// Log log = EasyMock.createMock(Log.class);
// Identity identity = EasyMock.createMock(Identity.class);
// Credentials credentials = EasyMock.createMock(Credentials.class);
//设置模仿对象中的状态和期望值
EasyMock.expect(credentials.getUsername()).andReturn("user").times(2);
//将模仿对象作为参数来调用域代码
TestUtils.setProperty(authenticator, "log", log);
TestUtils.setProperty(authenticator, "identity", identity);
TestUtils.setProperty(authenticator, "credentials", credentials);
//切换到replay状态
EasyMock.replay(credentials);
boolean result = authenticator.authenticate();
assert !result;
//验证模仿对象中的一致性
EasyMock.verify(credentials);
}
@Test
public void testAuthenticateWithAdmin() throws Exception {
// Log log = EasyMock.createMock(Log.class);
// Identity identity = EasyMock.createMock(Identity.class);
// Credentials credentials = EasyMock.createMock(Credentials.class);
EasyMock.expect(credentials.getUsername()).andReturn("admin").times(2);
TestUtils.setProperty(authenticator, "log", log);
TestUtils.setProperty(authenticator, "identity", identity);
TestUtils.setProperty(authenticator, "credentials", credentials);
EasyMock.replay(credentials);
boolean result = authenticator.authenticate();
assert result;
EasyMock.verify(credentials);
}
}
List 3:辅助类 TestUtils.java
package com.rolyer.sms.util;
import java.lang.reflect.Field;
public class TestUtils {
public static void setProperty(Object obj, String propertyName,
Object property) throws Exception {
Class<?> usersDAOClazz = obj.getClass();
Field emField = usersDAOClazz.getDeclaredField(propertyName);
emField.setAccessible(true);
emField.set(obj, property);
}
}
注:Authenticator.java是JBoss Seam的登录代码。
Unit Testing With Mock
Agenda
- What is mocking?
- Why to use mocking?
- Types of mocking
- Easymock
- Simple Examples of EasyMock(with extensions) and PowerMock
What is mocking?
- Mocking allows you to test a class or method in isolation .
- A class/method maybe dependent on many other classes/methods. It is with the help of others(collaborators) a class/method completes itz functionality.
- In mocking we replace all of its collaborators with mocks that essentially simulate the normal environment of the class/method.
- In other words: “A mock object is a dummy interface or class in which you define the dummy output of a certain method call.”
Why to use mocking?
- In mocking we replace all of its collaborators with mocks;
- DATABASE is not required so NO dbunit;
- Use of Context is avoided;
- Dependencies can be ignored;
- We just test the core functinality of the class/method and we dont have to test the collaborators;
- Easy;
- less time to write junit for complex legacy code.
How Mock Objects Work?
2 Types in general
- Proxy based like easymock,jmock
- remap the class file in the class loader like jmockit ,powermock
1. Proxy based
- Reflection : Java's Reflection API's makes it possible to inspect classes, interfaces, fields and methods at runtime, without knowing the names of the classes, methods etc. at compile time.
- It is also possible to instantiate new objects, invoke methods and get/set field values using reflection.
- Using Java Reflection you create dynamic implementations of interfaces at runtime. You do so using the class java.lang.reflect.Proxy
- you can set what return values the proxy must return.
- Proxy is a special class that allows intercept a set of methods identified by an interface public Object invoke(Object proxy, Method method, Object[] args)
- a proxy object is used to imitate the real object your code is dependent on.
2. Remap the class file
you tell the class loader to remap the reference to the class file it will load. So let's say that I have a class Employeee with the corresponding .class file called Employeee.class and I want to mock it to use MyMock instead. By using this type of mock objects, you will actually remap in the classloader the reference from Employeee to MyMock.class. Uses the Instrumentation API.
What is EasyMock?
- http://easymock.org/
- easymock framework helps you create mock objects.
- Others are : jmock / Mockito / rMock / jMockit / sevenMock
How to use easymock?
- Create a mock SomeInterface mock = createMock ( SomeInterface.class );
- Record behavior expect( mock.doStuff( "argument" )).andReturn( "returnValue" );
- Replay behaviour replay( mock );
- Executing the code we want to test String newValue=test.perform() assume perform() method calls doStuff() method.
- Verify behaviour verify(mock).
Step 1 : Create a Mock
- Identify the classes/methods which are the collaborators.We can mock the classes by first mockin the Interface
- createMock method is used to create the mock object. stmtMock = createMock ( Statement.class ) ;
Step 2 : Mock the methods
- In EasyMock we use a record / replay approach.
- record means -->You first train the mock by making the expected method calls on it .
- replay means -->it tells EasyMock to stop recording . After that, it calls on the object to return the set values. If it gets a call it does not expect, it throws an Exception to fail fast.
- It does not actually replay the mock. BUT it resets the mock so that the next time its methods are called it will begin replaying. st.executeQuery("SELECT * FROM survey"); expect(stmtMock.executeQuery("SELECT * FROM survey")).andReturn(rsMock);
Step 3 : Execute the code to test
- AssertEquals ( 55 , idao.getNoOfColumns() );
Step 4 : Verify the behaviour
- verify means-->verify( ) method checks to see if the mock actually received all the calls you expect
- The method is used to make sure that all of the behaviour expected from the collaborators is valid. verify (stmtMock);
Syntax
- To Throw An Exception
expect(mock.method()).andThrow(expectedException);
- Expecting A Method With void Return Type
mock.clear();
ExpectLastCall();
- Need to mention no. Of calls
expectLastCall().times(3);
- Argument Matchers
eq(X value)
isA(Class clazz)
anyBoolean(), anyByte(), anyChar(), anyDouble(), anyFloat(), anyInt(),anyLong(), anyObject(), anyShort()
Drawbacks of easymock
- Cannot mock static methods
- Cannot mock private methods
- Cannot mock final methods
- Cannot mock concrete classes
- Cannot mock Constructors
- jMock does not force us to put expectations on things that are irrelevant to the test at hand. If we're testing Cache.add(), it does not matter what the underlying call to Map.put() returns. With EasyMock, we have to fully specify everything, whether it matters or not
Types of objects in easymock
- Normal — createMock(): All of the expected methods must be called with the specified arguments. However, the order in which these methods are called does not matter. Calls to unexpected methods cause the test to fail.
- Strict — createStrictMock(): All expected methods must be called with the expected arguments, in a specified order. Calls to unexpected methods cause the test to fail.
- Nice — createNiceMock(): All expected methods must be called with the specified arguments in any order. Calls to unexpected methods do not cause the test to fail. Nice mocks supply reasonable defaults for methods you don't explicitly mock. Methods that return numbers return 0. Methods that return booleans return false. Methods that return objects return null.
Use of verify
- The verify phase confirms the execution of the expected calls.
- Useful in case of strict Mocks,where you need to check whether you called the methods,in right order.It will show all missing method calls.
- In case of regular mocks it is not necessary.
Repeated calls
- For every method invoked by the mock we need an expectation.
- By default mock expects minimum 1 call.
- times(int min, int max) : to expect between min and max calls,
- AtLeastOnce() : to expect at least one call, and
- AnyTimes() : to expected an unrestricted number of calls.
Matchers in EasyMock
- eq(X value) : Matches if the actual value is equals the expected value. Available for all primitive types and for objects.
- IsNull() : Matches if the actual value is null. Available for objects.
- NotNull() : Matches if the actual value is not null. Available for objects.
- startsWith(String prefix), contains(String substring), endsWith(String suffix) : Matches if the actual value starts with/contains/ends with the given value. Available for Strings.
- matches(String regex), find(String regex) : Matches if the actual value/a substring of the actual value matches the given regular expression. Available for Strings.