With the extensive promotion of Refactoring technology and XP software engineering technology, the role of unit testing is increasingly important in software engineering, and a concise cyclic, wide, efficient and stable unit test framework has a successful implementation unit test. critical use. In the Java programming statement environment, JunitFramework is an excellent test framework that has been adopted and empirically adopted by most Java programmers, but most of the programmers who don't try JUnit Framework are learning how JUnit Framework is to write unit tests that adapt to their development projects. Still feeling that there is a certain difficulty, this may be because JUnit has a focus on the user guide and documentation with the framework code and utility tool lies in explaining the design method of the unit test framework and a simple type of use instructions, while the framework in a specific test ( How to implement unit testing under JUnit, how to update and maintain existing unit test code during project development has not been detailed. Therefore, this document has further supplemented and description of the documentation attached to JUnit, so that JUnit can be used by more development teams, allowing unit testing and even refactoring, XP technology better in more development teams . 1. Writing Principles of Unit Tests JUnit The unit test listed in the document has certain confusion, because almost all sample units are a method for an object, and it seems that JUnit's unit test is only available for class organizational structure. Static constraints, thereby causing beginners to doubt the effects of unit testing under JUnit. So we need to redefine how to determine valuable unit tests and how to write these units tests, maintain these unit tests, so that more programmers accept and familiarize with the unit test under junit. At the design of the JUnit unit test framework, the author sets three overall goals, the first is to simplify the preparation of the test, this simplification includes the study of the test framework and the preparation of the actual test unit; the second is to make the test unit Maintain persistence; the third is to use existing tests to write related tests. It can be seen from these three objectives that the basic design considerations of the unit test framework are still from our existing test methods and methods, but only make the test make it easier to implement and extend and maintain persistence. Therefore, the principle of writing unit tests can be drawn and utilized from the test methods we usually used. 2. How to determine unit testing in our usual test, a unit test is generally for a specific feature of a particular object, for example, we have written a package implementation of a connection pool access to a specific database, we will establish the following Unit test: After the connection pool is started, whether the corresponding number of database connections apply in the pool according to the defined rules, whether or not to obtain a cache connection from the pool according to the defined rule, or establish a new connection release After a database connection, the connection is released or caching according to the defined rule is released or caching in the future, if the background Housekeeping thread is used, if the expiration of the expiration is released, if the connection has time period, the background Housekeeping thread is periodically released. The expired cache connection. Only some of the possible tests are listed here, but we can see the particle size of the unit test from this list. A unit test is basically based on a clear characteristic of an object, and the process of unit testing should be limited to a clear thread range. Based on, the test process of a unit test is very similar to a definition of a USE CASE, but the particle size of the unit test is generally smaller than the definition of USE CASE, because USE Case is separate Based on the transaction unit, the unit test is based on a particular feature of a strong object, and in general, many system features will be used to complete specific software requirements. From the above analysis we can concatenation, the test unit should be converted to a basic write unit with an internal state of an object.
A software system is the same as a design, the system's state is determined by the status of each discrete component within the system, so in order to determine that a system final behavior is in line with our initial requirements, we first It is necessary to ensure that the status of each part in the system meets our design requirements, so our test unit should focus on the status transformation of the object. However, it is not necessary to note that all object group features need to be written as a separate test unit, how to filter the principle of valuable test units in the object group feature in JUnitTest Infected: Programmers Love Writing Tests, I get the correct description You should introduce test units in places that are likely to introduce errors, usually in code logic that is more frequent with specific boundary conditions, complex algorithms, and demand variations. In addition to these features need to be written into an independent test unit, there are some border conditions that are more complex, and should be written as a separate test unit, which is already better description and explanation in the JUnit document. . We should also ask yourself: write these tests, we can check themselves if we have the test, if the code has passed these unit tests, we can determine the operation of the program is correct, in line with Demand. If we can't determine it, you should look at whether there is an omission of the unit test that needs to be written or re-examines our understanding of software requirements. Generally, more unit tests are always not wrong when starting using unit testing. Once we determine the test unit that needs to be written, it should be 3. How to write unit tests In XP, emphasize that the unit test must be written by the writer of the package package, which is required for the test goal we set. Because only this, the test can ensure that the object's runtime behavior meets the requirements, but only through the test of the class interface, we can only ensure that the object is in line with static constraints, so this requires us to open certain internal data during the test. Structure, or establish appropriate data records for specific operating behaviors and expose these data to a specific test unit. This means that we must modify the corresponding class packages when writing unit tests, and such modifications also happen in our previous test methods, so the previous test tags and other test techniques can still improve them in JUnit testing. use. Since the overall goal of the unit test is to be correct in our software in the operation, we need to guarantee the static constraints of the class in our design, but we need to guarantee the object. The operating status under specific conditions meets our preset. Or explain the example of the database buffer pool, a buffer pool is exposed to other objects, including the interface, including the parameter setting of the pool, the initialization of the pool, the destruction of the pool, and get a data connection from this pool and release Connecting to the pool, the change in the internal state of the pool is not needed with other objects. This is also in line with the principle of packaging. However, the status of pool objects, such as: The number of cached connections increases under certain conditions, and a connection is completely released after a long enough run, so that the connection is updated, and although the external object does not need to be clear. But it is the correct guarantee of the program running, so our unit test must ensure that these internal logic is run correctly. The test and debugging of compile language is difficult to track the logical process of running, but we know, regardless of how logic run, if the state's conversion meets our behavior settings, the verification results are obviously correct, so it is in one When the object is tested, we need to analyze and compute with most status conversions to verify the behavior of the object.
The state is described by a series of state data, so the writing unit tests first analyzes the change process of the state (the state conversion graph is very clear), and then determines the status data of the analysis according to the definition of the status, and finally provides Access to these internal state data. In the example of the database connection pool, we analyzed the status transformation of the pool-implemented object DefaultConnectionProxy, we decided to disclose the OracleConnectionCacheImpl object of the characterization status to the test class. See example an example one / *** This class is simple to package Oracle's implementation of the data connection buffer pool. ** / public class DefaultConnectionProxy extends ConnectionProxy {private static final String name = "Default Connection Proxy"; private static final String description = "simple wrapper class implementation oracle data connection pool."; Private static final String author = "Ion-Global.com"; private static final int major_version = 0; private static final int minor_version = 9; private static final boolean pooled = true; private connectionBroker connectionBroker = null; private Properties props; private Properties propDescriptions; private Object initLock = New Object (); // Test Code Begin ... / * In order to understand the status of the object, provide a public access interface (or providing the same class package) to some private variables that characterize the internal state of the identification object (or provide the same class package) The access interface is to enable the test unit to effectively determine the status shift of the object, and provide an access interface to the packaged OracleConnectionCacheImpl object in this example. * / OracleConnectionCacheImpl getConnectionCache () {if (connectionBroker == null) {throw new IllegalStateException ( "You need start the server first.");} Return connectionBroker.getConnectionCache ();} // Test Code End ... disclosed in the interior After the status data, we can write our test unit, the selection method of the unit test has been described in the previous chapter of this article, but still need to be noted that because the assert method will throw an error, you should The final set of test methods is judged by the ASSERT related way, which ensures that resources are released. For examples of the database connection pool, we can establish a test class defaultConnectionProxytest, and establish several TEST CASEs, as shown in the following example 2 / *** This class is simple to test.
** / public class DefaultConnectionProxyTest extends TestCase {private DefaultConnectionProxy conProxy = null; private OracleConnectionCacheImpl cacheImpl = null; private Connection con = null; / ** set the test fixture, the test start to establish the necessary environment. * / protected void setup () {conproxy = new defaultConnectionProxy (); conproxy.start (); cacheImpl = conproxy.getConnectionCache ();} / ** Test after service startup, check if it is After the service is started, the parameter setting of the connection pool is correct. * / Public void testConnectionProxyStart () {int minConnections = 0; int maxConnections = 0; assertNotNull (cacheImpl); try {minConnections = Integer.parseInt (PropertyManager.getProperty ( "DefaultConnectionProxy.minConnections")); maxConnections = Integer.parseInt (PropertyManager .getProperty ( "DefaultConnectionProxy.maxConnections"));} catch (Exception e) {// ignore the exception} assertEquals (cacheImpl.getMinLimit (), minConnections); assertEquals (cacheImpl.getMaxLimit (), maxConnections); assertEquals (cacheImpl. GetCachesize (), MINCONNECTIONS;} / ** Test the object of the sample 1 to get the database connection test, see if a valid database connection can be obtained, and after the connection is obtained, the status of the connection pool is in accordance with the established strategy. Change. Since the assert method throws an Error object, the ASSERT method is placed as a final collective of the method, so that the resources open within the method can be effectively closed correctly.
* / Public void testGetConnection () {int cacheSize = cacheImpl.getCacheSize (); int activeSize = cacheImpl.getActiveSize (); int cacheSizeAfter = 0; int activeSizeAfter = 0; con = conProxy.getConnection ();! If (con = null ) {ActiveSizeAfter = cacheImpl.getActiveSize (); cachesizeafter = cacheimpl.getCachesize (); try {con.close ();} catch (sqlexception e) {}} else {assertnotnull (con);} / * If in the connection pool The actual use of connections is less than the number of cache connections, check whether the obtained new data connection is obtained from the cache, and the pool is connected to establish a new connection * / if (cachesize> ActiveSize) {Assertequals (ActiveSize 1, ActiveSizeAfter; assertequals); assertequals Cachesize, cachesizeafter;} else {assertequals (ActiveSize 1, cachesizeafter;}} / ** Test of the object connection release, see if the connection of the connection pool is established in accordance with the established strategy after the connection is released Change. Since the assert method throws an Error object, the ASSERT method is placed as a final collective of the method, so that the resources open within the method can be effectively closed correctly. * / Public void testConnectionClose () {int minConnections = cacheImpl.getMinLimit (); int cacheSize = 0; int activeSize = 0; int cacheSizeAfter = 0; int activeSizeAfter = 0; con = conProxy.getConnection (); if (con =! null) {cacheSize = cacheImpl.getCacheSize (); activeSize = cacheImpl.getActiveSize (); try {con.close ();} catch (SQLException e) {} activeSizeAfter = cacheImpl.getActiveSize (); cacheSizeAfter = cacheImpl.getCacheSize () } else {assertnotnull (con);} assertequals (ActiveSize, ActiveSizeAfter 1); / * If the number of cache connections in the connection pool is greater than the minimum cache connection, check whether the cache connection after the release data is reducing one, Conversely, the number of cache connections remains to minimize cache connections * / if (cachesize> minConnections) {Assertequals (cachesize, cachesizeafter 1);} else {assertequals (cachesize, minconnections);} / ** Release establishment test starting environment Time resources.