JUnit source code is the first Open-source project I have read carefully. The reading master writes the code to be able to learn some good programming style and the realization thought, this is enhances own programming level to be effective method, therefore already wanted to see these prestigious framework is what is going on. Today take the simplest junit start, also calculate the beginning of their own source code analysis of the road.
JUnit, as the most famous unit testing framework, is a collaboration of two industry-renowned people who have undergone multiple versions upgrades (learn about JUnit basics, junit practices). JUnit is short and lean overall, and there are many lessons to learn from, but there are some deficiencies, of course, which are unavoidable for any program.
Below we will analyze the junit source from the whole (macroscopic) and detail (microscopic) Two aspects, the following analysis is based on 3.8.1 version.
Second, macroscopic--structure and model
Open the source file and you will find that the JUnit source code is assigned to 6 packages: Junit.awtui, Junit.swingui, Junit.textui, Junit.extensions, Junit.framework, Junit.runner. The first three packages include the JUnit Runtime entry program and the run results display interface, which is basically transparent to junit users. The Junit.runner package contains some of the underlying classes that support unit test runs and its own classloader, which is completely transparent to junit users.
The remaining two packages are closely linked to unit testing using JUnit. The junit.framework contains JUnit classes that must be used to write generic JUnit unit test classes, while Junit.extensions is a functional extension of the framework package as well as interfaces left for more functional extensions.
JUnit advocates simplicity and automation of unit testing. This requires that the use of junit be simplified and that automated testing is easy to implement. The whole junit design is probably following that premise, too. The backbone of the entire framework consists of only three classes (as shown in the following illustration).
If you have mastered the way testcase, TestSuite, and basetestrunner work, then you can write the test code as you like.
Let's take a look at the relationship between classes in Junit.framework, the figure below is what I analyzed from the source code and most of the relationships are represented.
Take a look at the responsibilities of each class first. The Assert class provides a complete set of assertions that JUnit uses, which are inherited by TestCase, and the assert becomes transparent. The test interface is designed to unify the types of testcase and Testsuite, while TestCase provides methods for running unit test classes, and in Testsuite provides methods for loading unit test classes, verifying test class formats, and so on. TestResult is the place to store test results, but in junit it comes with a bit of controller functionality.
Here's where I think something is wrong. There is a two-way dependency between testcase and TestResult on the diagram, and in the relationship of UML class diagrams It is pointed out that dependencies are always one-way. Let's take a look at this suspicious place.
Code in TestCase:
/**
* Runs the test case and collects the results in TestResult.
*/
public void Run (TestResult result) {
Called the Run method in result,
TestResult by name should be a record of the test results of the class, how can still run?
Result.run (this);
}
corresponding to the code in TestResult:
/**
* Runs a TestCase.
*/
protected void Run (final TestCase test) {
Start testing
Starttest (test);
This anonymous inner class is used for a while to speak again
protectable p= New protectable () {
public void Protect () throws Throwable {
Oh, God, here's the Runbare method in TestCase.
Test.runbare ();
}
};
runprotected (test, p); The method is to perform the anonymous inner class set above
Endtest (test);
}
Runprotected method in TestResult:
public void runprotected (final Test test, protectable p) {
try {
P.protect ();
}
catch (Assertionfailederror e) {
Addfailure (test, E); Add a failure record to TestResult
}
catch (Threaddeath e) {//don ' t catch Threaddeath by accident
Throw e;
}
catch (Throwable e) {
Adderror (test, E); Add error logging to TestResult
}
}
Why are there such strange dependencies in JUnit, and testresult that violate the principle of single responsibility? When I saw the testsetup in the junit.extentions bag, maybe I guessed the author's intention. Let's take a look at the code in Testsetup:
public void Run (final testresult result) {
And I saw the anonymous inner class like above.
protectable p= New protectable () {
public void Protect () throws Exception {
But the implementation in this inner class is different.
SetUp ();
Basicrun (result);
Teardown ();
}
};
The Runprotected method in TestResult is invoked to perform the above implementation
Result.runprotected (this, p);
}
This class is created to make up for a small flaw in the TestCase class (see the next section for details). Notice that there are also anonymous inner classes similar to TestResult in this class. This anonymous inner class is full of nameless implementations of the protected interface, where I think there are two points:
1 because the inner class can be completely invisible in the next scenario and is not used by anyone, it hides the implementation details of the interface.
2 in order to improve the reusability, and the use of internal classes is relatively fast. So no matter what you protect in the method, the code for its error, failure, and exception capture (the Runprotected method in TestResult) can be reused.
This is why there is such a strange dependency: in order to reuse, let the runprotected method be placed in a place that testcase and Testsetup can call.
However, I think it is necessary to carefully consider the damage to the system's good structure and readability for reuse. The design of JUnit is estimated to be considered for reuse after multiple extensions of the framework.
I'm finished with the question that puzzles me. What I think is most impressive about the JUnit framework is that there are a lot of design patterns inside the small frame. And the use of these patterns is to reflect the overall framework of the structure of the concise, scalable. I will make a rough analysis of the following (the details of the pattern application please pay attention to my article on design patterns). First look at the design patterns used in junit.framework.
Command mode: As a framework for secondary unit testing, developers should only be concerned with the writing of test cases when using it, and JUnit is just a test case executor and result viewer, and should not care too much about the details of the framework. For JUnit, however, it does not need to know the operation information of the request TestCase, only executes it as a command, and then sends the execution test results to the developer. Command mode is precisely to achieve this kind of send coupling purpose.
Combination mode: When the test cases of the system become more and more, running test cases one after another becomes a thorny problem. As an Easy-to-use unit testing framework, this must be addressed. As a result, JUnit provides testsuite functionality that allows multiple test cases to be executed in one testsuite, and further supports Testsuite's set of Testsuite functions. Using the combination mode can be a good solution to this problem.
The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion;
products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the
content of the page makes you feel confusing, please write us an email, we will handle the problem
within 5 days after receiving your email.
If you find any instances of plagiarism from the community, please send an email to:
info-contact@alibabacloud.com
and provide relevant evidence. A staff member will contact you within 5 working days.