Write unit test with JUnit Framework

xiaoxiao2021-03-06  81

Write the unit test Shen Wenbo (from IBM DW China) with JUnit Framework (from IBM DW China) December 01, 2002

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, Junit Framework is an excellent test framework that has been adopted and empirically used by most Java programmers, but most of the number of programs that do not try JUnit Framework are learning how JUnit Framework is to write unit tests that adapt to their own development projects. I still feel that there is a certain difficulty, which may be because JUnit has the user guide and documentation with the framework code and utility with a focus on explaining the design method of the unit test framework and a simple type of use instructions, while the framework is in a specific test framework. Under how to implement unit testing, how to update and maintain existing unit test code during project development has not been explained in detail. 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 .

Writing principle of unit test

The unit test listed in JUnit has a certain confusion because almost all sample units are a method for an object, and it seems that the unit test of JUnit is only suitable for static constraints of class organizational structures. Scholar doubts 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 units tests, so that more programmers accept and familiar with the Writing of 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, which includes the study of the test framework and the preparation of the actual test unit;

The second is to keep the test unit persistence;

The third is to use existing tests to write relevant 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.

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 the connection pool access to a specific database, we will establish the following unit test:

• Whether the corresponding number of database connections are established in the pool according to the defined rules after the connection pool is launched;

· Apply for a database connection, whether to obtain a cache connection directly from the pool according to the defined rules, or establish a new connection;

· After releaseing a database connection, the connection is released or cache according to the defined rules for future use;

· Background Housekeeping thread is released in accordance with the defined rules;

· If there is a time limit, whether the background Housekeeping thread is periodically released, the expired cache connection is released;

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, then you should:

How to write unit tests

Under XP, the unit test must be written by the writer of the class package, which is necessary for the test goal of our setting. 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 one

Example 1 / *** 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 = "This class is simple to package Oracle's implementation of the data connection buffer 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, it is necessary to provide a public access interface (or provide access to the same class package) in order to make the test unit may Effectively determine the status transition of the object, and provides 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 ...

After disclosing internal state data, we can write our test unit, and the selection method of unit testing and selection scale have been described in the previous section of this article. But still need to be noted that because the assert method throws an error, you should use the ASSERT related method in the final set of test methods to ensure that resources are released. For examples of database connection pools, we can establish test class defaultconnectionProxytest, and establish several TEST CASEs, as follows:

Example 2 / *** This class is a simple test of the class in the sample one. * * / 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.

* / protected void teardown () {cacheiMPL = null; conproxy.destroy ();} public defaultConnectionProxytest (String name) {super (name);} / ** You can simply run this class to test the test included in the class The unit is tested. * / public static void main (string args []) {junit.textui.teStrunner.run (default ";}} When the unit test is complete, we can organize the test unit with the test unit provided by JUnit, you can Decide the order of the test and then run your test.

How to maintain unit testing

Through the above description, we have basic understanding of how to determine and write tests, but demand always changes, so our unit testing will continue to evolve according to the changes in demand. If we decide to modify the behavior rules of the class, it can be clear that we will certainly modify the test unit for this class to accommodate changes. However, if there is no change in the behavior of this class only if there is a call relationship, the corresponding unit test is still reliable and sufficient, and if the status definition of the object of the class containing behavior changes, the test unit still starts. effect. This result is also the advantage of the package principle.

About author

Shen Wenbo: Born in 1973, is now a senior technical consultant, Aon Technology Shanghai. There are longer work experience in relational database object modeling, familiar with Java language, and currently engaged in the works of OOA, OOD, and enterprise applications.

转载请注明原文地址:https://www.9cbs.com/read-108462.html

New Post(0)