锘??xml version="1.0" encoding="utf-8" standalone="yes"?> I'm a big fan of Test-driven development Reinventing a wheel is great for learning, and I've done it a lot.
But perhaps in a sign that I'm getting older, I'm more concerned with
getting stuff done, so now I tend to lean towards existing solutions
when good ones exist. I searched for what people tend to use for mock
objects and jMock The general pattern with creating mocks for a tier of your
application is that you're trusting that that tier is tested and works
correctly, so it's appropriate to have mocks return hard-coded values.
We're testing that the caller of the mocked interface does the right
thing with correct (or incorrect) inputs, so mocks help us to focus on
what's being tested in isolation. I'd always found it difficult to unit test Spring The reality is that we do too much business logic in controllers,
and usually it's lazyness but often it's being pragmatic. We also tend
to often do even more "illegal" activities in controllers, such as
writing directly to the response rather than delegating to a It's helpful to have partially-functioning servlet container classes such as The syntax takes a while to get used to. Basically the flow that I tend to use is: As simple as that may sound, it can look very weird at first. Say I have an interface: I can create a mock for it via: Then I can register expected calls via This says that in my calling code, when the client calls I can then inject this mock into the class being tested in place of
the real one, trusting that it will return known results. I can then
concentrate on assuring that the tested class does the right thing. Thanks to EasyMock, my productivity for testing services and controllers is way up - and my code coverage percentages are too.
This entry was posted on Monday, August 13th, 2007 at 2:43am
and is filed under easymock, java, junit.
You can follow any responses to this entry through the RSS 2.0 feed.
You can skip to the end and leave a response. Pinging is currently not allowed. Source from http://burtbeckwith.com/blog/?p=43
I have a method that returns void in a class that is a dependency of the class I want to test. This class is huge and I'm only using this single method from it.
I need to replace the implementation of this method for the test as I
want it to do something different and I need to be able to access the
parameters this method receives. I cannot find a way of doing this in EasyMock. I think I know how to
do it with Mockito by using doAnswer but I don't want to add another
library unless absolutely necessary.
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class ConcurrentTest {
private static int thread_num = 200;
private static int client_num = 460;
private static Map keywordMap = new HashMap();
static {
try {
InputStreamReader isr = new InputStreamReader(new FileInputStream(
new File("clicks.txt")), "GBK");
BufferedReader buffer = new BufferedReader(isr);
String line = "";
while ((line = buffer.readLine()) != null) {
keywordMap.put(line.substring(0, line.lastIndexOf(":")), "");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
int size = keywordMap.size();
// TODO Auto-generated method stub
ExecutorService exec = Executors.newCachedThreadPool();
// 50涓嚎紼嬪彲浠ュ悓鏃惰闂?/span>
final Semaphore semp = new Semaphore(thread_num);
// 妯℃嫙2000涓鎴風璁塊棶
for (int index = 0; index < client_num; index++) {
final int NO = index;
Runnable run = new Runnable() {
public void run() {
try {
// 鑾峰彇璁稿彲
semp.acquire();
System.out.println("Thread:" + NO);
String host = "http://10.99.23.42:7001/KMQueryCenter/query.do?";
String para = "method=getQueryResult&pageNum=1&pageSize=5&"
+ "queryKeyWord="
+ getRandomSearchKey(NO)
+ "&questionID=-1&questionIdPath=-1&searchType=1"
+ "&proLine=&proSeries=&proType=" + NO;
System.out.println(host + para);
URL url = new URL(host);// 姝ゅ濉啓渚涙祴璇曠殑url
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
// connection.setRequestMethod("POST");
// connection.setRequestProperty("Proxy-Connection",
// "Keep-Alive");
connection.setDoOutput(true);
connection.setDoInput(true);
PrintWriter out = new PrintWriter(connection
.getOutputStream());
out.print(para);
out.flush();
out.close();
BufferedReader in = new BufferedReader(
new InputStreamReader(connection
.getInputStream()));
String line = "";
String result = "";
while ((line = in.readLine()) != null) {
result += line;
}
// System.out.println(result);
// Thread.sleep((long) (Math.random()) * 1000);
// 閲婃斁
System.out.println("絎細" + NO + " 涓?/span>");
semp.release();
} catch (Exception e) {
e.printStackTrace();
}
}
};
exec.execute(run);
}
// 閫鍑虹嚎紼嬫睜
exec.shutdown();
}
private static String getRandomSearchKey(final int no) {
String ret = "";
int size = keywordMap.size();
// int wanna = (int) (Math.random()) * (size - 1);
ret = (keywordMap.entrySet().toArray())[no].toString();
ret = ret.substring(0, ret.lastIndexOf("="));
System.out.println("\t" + ret);
return ret;
}
}
濡傛灉鏈枃瀵規(guī)偍鏈夊府鍔╁茍涓旇榧撳姳鎴戠殑璇濓紝璇鋒壂鎻忓涓嬩簩緇寸爜鏀寔鏈漢鐨勫姵鍔ㄦ垚鏋滐紝澶氳阿浜嗭紒
]]>
]]>Fun with EasyMock
and have been using JUnit
for 6 or 7 years (since around v3.4 or v3.5). I've written lots of mock
objects (from simplistic dummy concrete classes that implement an
entire interface, to Proxy
-based
mocks, to a pretty decent mock JMS library) and played with various
mock libraries, but the one I'm sticking with and using almost
exclusively is EasyMock
.
and EasyMock are popular. I prefer the method-based approach of
EasyMock to the string-based approach of jMock, since code changes will
cause the tests to break immediately, and the mocks will be refactored
along with the rest of the code if I use the IDE's refactoring features.
MVC
controllers, since it seems like they need a running container, and I don't want to deal with the hassle of setting up Cactus
.
Plus it seems somewhat useless to test controllers, since they
shouldn't be doing much that isn't tested in proper automated
functional tests. If they do too much business logic, they should be
refactored to push that work out to the services layer, right?
View
. This is usually the case for XML and JSON generated for Ajax responses. So until these get refactored, they need to be tested.HttpServletRequest
and HttpServletResponse
,
and the Spring Mock library (spring-mock.jar) is great for that. But
for creating mocks for DAOs, services, and other interface-based
dependency-injected resources, EasyMock greatly simplifies things. And
with their user-contributed Class Extension, you can even mock out concrete classes.
expect(mock.[method call]).andReturn([result])
for each expected callmock.[method call]
, then EasyMock.expectLastCall()
for each expected void callreplay(mock)
to switch from "record" mode to "playback" modeverify(mock)
to assure that all expected calls happened
thing.doSomethingElse("woot", 5)
to return 123, when the client calls thing.doSomethingElse("fubar", 45)
to return 321, and to expect a call to thing.doSomething("p")
.
]]>
mockObject.someMethod(eq(param1), eq(param2);
expectLastCall().andAnswer(newIAnswer(){
publicObject answer(){
//supply your mock implementation here
SomeClass arg1 =(SomeClass) getCurrentArguments()[0];
AnotherClass arg2 =(AnotherClass) getCurrentArguments()[1];
arg1.doSomething(blah);
//return the value to be returned by the method (null for void)
returnnull;
}
});
]]>
It is however impossible to mock static method calls like FacesContext.getCurrentInstance(). So how do we go around testing these calls out of container? We've thought up three different approaches, but each has its pros and cons. Let us first take a look at these three approaches...
1. Extract method
Introduce a method in each class that looks like the following:
protected FacesContext getFacesContext() {
return FacesContext.getCurrentInstance();
}
This can be easily mocked out in your test by creating the class with an overridden method, as follows:
FacesContext mockContext = EasyMock.createMock(FacesContext.class);
new MyObjectUnderTest() {
protected FacesContext getFacesContext() {
return mockContext;
}
}
Pros
Cons
2. Static Helper
Our second option is to introduce a static helper class which has the following implementation:
public class FacesContextHelper {
private static FacesContext context;
public static FacesContext getCurrentFacesContext() {
return context != null ? context : FacesContext.getCurrentInstance();
}
public static void setFacesContext(FacesContext facesContext) {
context = facesContext;
}
public static void reset() {
context = null;
}
}
Now in a testclass we can just write down the following line to mock out the FacesContext:
FacesContextHelper.setFacesContext(EasyMock.createMock(FacesContext.class));
Pros
Cons
3. OO Helper class
Our third and last option is to introduce an interface and implementation of the FacesContextHelper:
public interface FacesContextHelper {
FacesContext getCurrentFacesContext();
}
public class FacesContextHelperImpl implements FacesContextHelper {
public FacesContext getCurrentFacesContext() {
return FacesContext.getCurrentInstance();
}
}
We now need to introduce an instance of the FacesContextHelperImpl to each class. For this example, we will use a package protected variable in each class. It is of course also possible to use a setter method. For our test cases we now need to introduce a new FacesContextHelper:
public class MockFacesContextHelper implements FacesContextHelper {
private FacesContext mockContext;
public MockFacesContextHelper(FacesContext mockContext) {
this.mockContext = mockContext;
}
public FacesContext getCurrentFacesContext() {
return this.mockContext;
}
}
In our test cases we can now easily mock out the FacesContext again by setting the package protected field:
someClazz.facesContextHelper = new MockFacesContextHelper(EasyMock.createMock(FacesContext.class));
Pros
Cons
That's it. I myself am still doubting as to which method is the lesser evil, not one feels absolutely right. What do you think, which method do you consider better? Did I overlook another option perhaps?