文|碼術張
本文旨在說明Junit4源代碼的設計思想。
說明方式上,使用的是自己的一個創(chuàng)新的想法:
用一個ut來說明嫡丙。
代碼如下;
package test;
import org.junit.Test;
public class IpTest {
@Test
public void should_True() throws Exception{
MyNotifier notifier = new MyNotifier();
MyResult result = new MyResult();
notifier.addListener(result.createListener());
notifier.addListener(new Detail());
MyDescription description = MyDescription.createMyDescriptionn("IpTest", "");
notifier.fireTestRunStarted(description);
MyDescription descriptionForA = MyDescription.createMyDescriptionn("IpTest", "methodA");
notifier.fireTestStarted(descriptionForA);
System.out.println("Hi..., I am method A");
notifier.fireTestFinished(descriptionForA);
MyDescription descriptionForB = MyDescription.createMyDescriptionn("IpTest", "methodB");
notifier.fireTestStarted(descriptionForB);
System.out.println("Hi..., I am method B");
notifier.fireTestFinished(descriptionForB);
notifier.fireTestRunFinished(result);
}
}
類圖:
類圖
交互圖:
交互圖
在class IpTest中棺榔,有一個should_True方法。
這個方法的實現(xiàn)败砂,即演示了Junit4的實現(xiàn)原理。
MyNotifier龄毡、MyResult吠卷、MyDescription,
對就Junit源碼中類Notifier沦零、Result祭隔、Description。
程序運行時,首先發(fā)布一個事件fireTestRunStarted疾渴;
運行結(jié)束時千贯,發(fā)布一個一個事件fireTestRunFinished。
類的每一個方法運行時搞坝,首先發(fā)布一個事件fireTestStarted搔谴;
類的每一個方法結(jié)束時,會發(fā)現(xiàn)一個事件fireTestFinished桩撮。
Listener收到事件后敦第,會做一些操作。
這些操作的結(jié)果會在程序結(jié)時店量,打印出來:
運行結(jié)果
在Junit源代碼中芜果,每個方法會有一個用職責鏈模式構(gòu)建一個Statement類的對象。
一個方法的執(zhí)行融师,即是依次執(zhí)行這個鏈表上的語句右钾。
這點沒在本文中體現(xiàn)。
本文所用的其他代碼如下:
package test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MyNotifier {
private final List<MyRunListener> fListeners =
Collections.synchronizedList(new ArrayList<MyRunListener>());
public void addListener(MyRunListener listener) {
fListeners.add(listener);
}
public void fireTestRunStarted(final MyDescription description) throws Exception {
for (MyRunListener each : fListeners) {
each.testRunStarted(description);
}
}
public void fireTestRunFinished(final MyResult result) throws Exception{
for (MyRunListener each : fListeners) {
each.testRunFinished(result);
}
}
public void fireTestStarted(final MyDescription description) throws Exception{
for (MyRunListener each : fListeners) {
each.testStarted(description);
}
}
public void fireTestFinished(final MyDescription description) throws Exception{
for (MyRunListener each : fListeners) {
each.testFinished(description);
}
}
}
package test;
import java.util.concurrent.atomic.AtomicInteger;
public class MyResult {
private AtomicInteger fCount = new AtomicInteger();
private long fRunTime = 0;
private long fStartTime;
public int getRunCount() {
return fCount.get();
}
public long getRunTime() {
return fRunTime;
}
private class Listener extends MyRunListener {
@Override
public void testRunStarted(MyDescription description) throws Exception {
fStartTime = System.currentTimeMillis();
}
@Override
public void testRunFinished(MyResult result) throws Exception {
long endTime = System.currentTimeMillis();
fRunTime += endTime - fStartTime;
}
@Override
public void testFinished(MyDescription description) throws Exception {
fCount.getAndIncrement();
}
}
public MyRunListener createListener() {
return new Listener();
}
}
package test;
import org.junit.runner.notification.Failure;
public class Detail extends MyRunListener {
public void testRunStarted(MyDescription description) throws Exception {
println("==>JUnit4 started with description: \n" + description);
println();
}
public void testRunFinished(MyResult result) throws Exception {
println("==>JUnit4 finished with result: \n" + describe(result));
}
public void testStarted(MyDescription description) throws Exception {
println("==>Test method started with description: " + description);
}
public void testFinished(MyDescription description) throws Exception {
println("==>Test method finished with description: " + description);
println();
}
public void testFailure(Failure failure) throws Exception {
println("==>Test method failed with failure: " + failure);
}
public void testAssumptionFailure(Failure failure) {
println("==>Test method assumption failed with failure: " + failure);
}
public void testIgnored(MyDescription description) throws Exception {
println("==>Test method ignored with description: " + description);
println();
}
private String describe(MyResult result) {
StringBuilder builder = new StringBuilder();
builder.append("\tRunCount: " + result.getRunCount())
.append("\n");
;
builder.append("\tRunTime: " + result.getRunTime())
.append("\n");
;
return builder.toString();
}
private void println() {
System.out.println();
}
private void println(String content) {
System.out.println(content);
}
}
package test;
public class MyDescription {
private String fclassName;
private String fMethodName;
public String getMethodName() {
return fMethodName;
}
public String getClassName() {
return fclassName;
}
@Override
public String toString() {
return getDisplayName();
}
/**
* @return a user-understandable label
*/
public String getDisplayName() {
return formatDisplayName(fMethodName, fclassName);
}
private MyDescription(String className, String methodName) {
fclassName = className;
fMethodName = methodName;
}
public static MyDescription createMyDescriptionn(String className, String methodName) {
return new MyDescription( className, methodName);
}
private static String formatDisplayName(String name, String className) {
return String.format("%s(%s)", name, className);
}
}