Talk about how to ensure the correctness of the test code (End)

zhaozj2021-02-16  56

This article is only in the unit test, although it is test, but the purpose is to drive development, but it is not to talk to the test drive development. What is the correctness of Test First First First First First First First First First First And the idea, of course, I think it is common, no matter whether it is test priority. And I have currently contacted the most Java unit test, so talking is the Java, and the example is related to Java.

In addition, some days have seen a post someone asked such a question, thinking that there is similar confusion when I just get in touch with the JUnit unit, now have some experience, so I wrote it, it is a summary of my experience, I hope someone can mutual Discuss the improvement.

The first thing I think is to do the correctness of the test code:

First, Test First

Second, only write demand test (note, it is the target code demand, this code is of course yourself, ^ _ ^)

Third, don't test it for testing (this sentence is when he chats with a friend, he said that he and Kent Back time Kent Back reminds him, here I don't have it to determine the correctness of this sentence, because Understand a sentence, the context is also critical, and I don't know much about my friend with the specific content and process of the Kent Back conversation, but here is still a point to talk about my own idea)

Fourth, write a little (atom) test each time

Five, Clean Code That Works, (of course, including test code, ^ _ ^)

Sixth, for an application framework, it is best to write a test framework for this frame (this is actually a very specific content, but now Java is much more used in the web, the test is relatively difficult, so there is this point)

Seven, always reminders yourself Test First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First First (Our purpose is to drive development, not for testing, huh, this is the first one and second point in front, the reason why it has to be alone, just reminding again, so this is not a detailed explanation)

Eight, lazy is a common problem of programmers, but there is no lazy. (I have this point of view: the level of programmers is high, in fact, from him to be lazy ... ^ _ ^)

Before I started specific to the above points, I would like to write an example first, just feel that I should write an example, ^ _ ^, my literary talent is really not very good, so I feel it.

one example:

I have a class specifically to parse the database operation result set (RESULTSET) into a Document object, which can be based on a sub-node of a defined node (Declare node) in a given XML configuration template (Column Node) Collection to parse the ResultSet generates the Document object, where each column node defines the properties of a certain field you want to get from the ResultSet, including the field name, whether the display is modified (editable), whether it has formatted mode properties (Pattern) Used to format the data of this field, etc. In order to do more common, it can generate a column node according to the ResultSetMetAdata object returned by ResultSet, and then obtain the data body by the column node, of course, this is very limited. For example, the setting of the Column node's Pattern, Editable, etc. will not be too flexible. This design, its maximum flexibility is placed on the XML configuration template, and the data is defined by the template, and the display attributes are defined, and once the column node is generated according to the given Resultset, the flexibility is greatly reduced. Although in most applications, it will not be generated by the ResultSet, but if the start does not determine the defined column, it is necessary to do so, especially the number of fields output in the resulting output is changed. The problem finally came out, recently an application is like this, first, you must use the ColumnEt acquisition node (Column), then after completing all the code, find a redundant field in a given RESULTSET, this time, No way, can only be modified to adapt to it. After encountering this trouble, and determine that you must modify your own code (honestly, the first react should be a modified SQL to remove the redundant field, because the original design is not redundant, but it is indeed everyone. There is a difficult experience, SQL can not change, because the implementation of the database end uses a stable public implementation, and fortunately, the redundant field is the same.), The first thought of my head is added. An event listener to monitor the data part of generating data in this app (in order to describe convenience, aunt called "Database), once the data generates triggers the result of ResultSet parsing, then I can write the result of the monitor to process the results Set (of course, it is to drop the redundant data CUT), so that other similar conditions will be displayed later, I can filter data by adding a new listener without having to move the original code. SWEAT, it seems that this idea is still possible, at least more than written a subclass, it seems simple, and it is easy to modify. (In fact, pay attention to the environment of this matter, first is that the code is OK, and then suddenly discovers this problem, and this problem is needed to change, so there is not much time to think about it, I always commit this. mistake.)

At this time, I have two options, one is to write the code directly, one is to write a test first. Of course, I chose to write a test, and the reason why these two of course are of course to compare. Let me say if I write the code first, then I will directly enter the role of the target function, because it seems that the target is clear, I want to add a function that can handle the listener with the ResultSet parser, trigger Analyze the completion event, inform all registered listeners, and pass the result of the resolution through the event object to the listener. In the past, I will immediately think of how to add an event, how to handle the list of events, and two necessary interfaces, one is a listener interface, one is an event interface, some brief ideas, do not need to complete these work, Then start debugging.

So above, mainly for comparison, now I write test. Of course, as described above, it seems to be very clear, then write a test class, after writing the necessary to do list, I start thinking if the code is finished, how do I use it? Oh, yes, the test is also written before testing, this implementation can be very simple, kill the resolution of the resulent Document, huh, the result is more easier, then all the nodes remove it. ^ _ ^. (Note: The test here has not yet reached the primary function, just do the test listener section first), this time, I suddenly feel that this design seems still still trouble (lazy is a common problem of programmer, Sweat, I want to be lazy every time I want to be lazy Every time I want to be lazy. This sentence is to write the monitor, but also let the DataBuilder process the listening list trigger event, although the filter data operation can be independently added, and the listener registration can also be done through the XML configuration file, but still more, it seems Currently, this demand is just a decorator mode, write a modified class to modify the resultSet parser is almost the same, and the new features will be needed. If you change Decorator, you can nearest multiple functions, this, no need Too much hand to Database. SWEAT, but you haven't worked hard (anyway, the test is prioritized to re-recognize your design, and the target, so I have no refuse to abandon the original idea). The new goal has appeared, it seems that I need a Decorator class implemented by a parser interface (Note: This resultset parser class is originally an implementation of an interface ResultSetParser), I can first write an abstract class of an extension parser interface to pack, The subsequent Decorator implementation is inherited from this abstract class, and it can be achieved directly (actually I have always thought that all Decorator implementation classes have a parent abstraction class inherit the interface of the modified target class. The most important The purpose is to make the Decorator implementation class more clearer, because this abstract class has something to pack, these moved to subclasses can be fully, such a class is to directly implement the interface of the modified target class, the effect is the same, So I think there is such an abstract class unified by all modified classes, which is more important to announce that a modified target class interface is a modified class, which can be used in reading procedures, and use the API. Good results.). OK, just do this (I am always very easy to decide, ^ _ ^).

So how do I test now? Of course, I also need a Decorator class implementation to test (or dropping Document's child node remove), after the test data is prepared, of course, the check result set, But this time is coming again, I will not only check if the column node is filtered correctly, but also check if the data body is correct, if I use a hard code assertion to test, then the result must be a bunch of assertions, of course, I can first write a correct result set XML file, then read it and the processing result is compared (, the Document object does not implement Equals), but this is not the trouble, the most trouble is, this test will Depending on the database environment, I have to save the database environment to ensure that I can reproduce my test in any case, which is undoubtedly the trouble, honestly, the reason why it will not write test (even if it is still not now Write, although I also clear the benefits of writing tests), mainly the database environment is difficult to build at any time (I have tried dbunit, it is not very good, the code of building the environment is also a lot), I am dizzy ( If you want to be lazy, do you sneak without writing a test?).

Write test is a very strange process, the mind of the brain and the direct write application code seems to be different, huh, I didn't figure it out, I think this is the difference between and master, but only can not be said, I have been The point of view is that people who really understand can explain the blurred things into clear concepts. In any case, when I am ready to start writing this painful test code, I have a new idea. Why don't you get redundant data? Although the application here is very attractive, it is enough. Simple, but,, at the same time, think of some of the Document object itself, first it is a big object (huh, I feel that DOM's object is very big, but it hasn't really understood it. It is a Dafa). Second, if it is Filter data, then it is necessary to traverse the Document node, which seems to handle the efficiency of it. It is better to get redundancy data from the Resultset at the beginning. As I thought, it is still very weak on the automatic generation of the Column node. You can refacture it with Builder mode. In this way, you can implement filtering by replacing the Column node's Builder, and now, it is true that only automatically generates column. The condition of the node needs to filter the redundancy data, so it is not necessary to consider the status of the Column node by the XML configuration template; and the original default generation method can be refined immediately as the default builder; this added code is actually very small. I want to be almost less, because it is necessary to traverse Document when processing redundant data, and now I will look for the field name obtained from ResultSetMetadata. There is no column node to filter, but there is a new When the change occurs, it can be replaced by replacing the Builder interface. Of course, this resultset parser is a test class, there is also a related test, then generates the column part of the code to extract it as a default Builder, you can run the previous test, ah, hahaha, this is hard to blow the ash, The reconstruction of Eclipse is easy to extract the code that generates Column's code into a method, then enhances this method into an interface, then this is so (without saying), you have a default builder implementation, then the parser adds a Builder class. Attribute, simultaneously generate its set method, all KO running test (here is the tool automatically do, I will be bigger, but more running test is always there is no harm, and I will run once again. In a few seconds, maybe it is not available, ^ _ ^), all the green lights. Reconstruction completion, SWEAT (notice, to it, in fact, I have not written test, but used the original test code, and the code part is also automatically generated. Most importantly, here, use Builder mode, Its interface has also been tested, so the Builder implementation after the rear is not considered, but only a complete functional demand test, the functional demand can always be very single, and naturally simplifies the test code).

Now I return to the beginning, my goal is changed, huh, huh, new goal: a Builder interface implementation, filtering redundant fields. How to test? A resultSet is necessary, if there is no redundant data, the field name is fixed in the actual application (Oh, don't forget my original purpose, it is filtering because SQL query uses the public interface The same redundant field appears.), I can select an empty result set, which is the field name I want to filter (here, as mentioned above, the data is not what I want to care, because of the data body According to the column node, I just check that the Column node is not right, this also simplifies my test code), of course, the Builder object of resetting the ResultSet parser is the target code class that will be implemented. Soon, all the code, run test, error, modification, run test, Kent Back test mode is so simple, and finally, finally all the way green light. Call .......... Oh, of course, I didn't forget the last step, Clean Code That Works. I will refine the original code in this state, there is a metaphor: turn the door to play the dog. At this time, the reconstruction can really say that it is only returned to the original land, but the usual results are always unexpected. Regardless, began to reconstruct, several critical reconstruction: First, the default Builder can use a Singleton mode, of course, the Builder that implements the redundant field is also because this code generated the COLUMN node is not subject to multi-threading; The builder that is then filtered with the redundancy field can actually be a default builder Decorator, three down five out two. Final test passed, check in code.

This is my experience. It can be seen that the earlier test code has also played, so as long as the code is still running, the test code will never be redundant. To maintain a clear test code, not only need clear demand analysis, design, and good original target code, a little lazy spirit, it is also necessary, ^ _ ^. In terms of demand analysis and design, the test gives priority has an irresistible force to let you in depth, especially for users of your target code to consider the use of these codes. Originally, I just want to mention it, I didn't expect so much, SWEAT.

OK, here start discussing one by one:

First, Test First

Test first, its core is not "Test", but "first". The purpose of testing drive development is to development, and the test is preferred. From the beginning of the test, the test is ended, the intermediate is a small incremental iterative development, test code and development code synchronization (Note: In fact, I think the test code is also the development code However, in order to distinguish these 2, the development code mentioned below refers to the test code part; in addition, this sentence itself is a problem, it is not comprehensive, just one aspect of test driver development. ).

It is not to ensure that the correctness of the test code is Test First, but Test First can maximize the correctness of the test code. (Here you have to return to the code itself, whether it is test code or development code, nothing more than a development language code, to ensure the correctness of the code, I think it is mainly a good design, clear structure, and reasonable use of the language, of which Keeping the code structure is the easiest to do, simple indentation, unified coding style is basically qualified. For test code, the same truth.) If you don't write test, then you must have a target code (nonsense) It seems like, ^ _ ^), this will inevitably be restricted when writing test code, don't say that you can ignore the target code (I think 90% is still more difficult, all write, Change to change, and once an interface is published, it is not allowed to change it. Testing (huh, it seems that if you write the development code first and then write the test code, the test code is mostly to verify the correctness of the development code, at least I have done this situation, so you are Thinking is imprisoned, the test code is good and bad (here is not to say, ^ _ ^), is controlled by the development code. Speaking back to me, if it is really because of the time relationship, as well as the sudden change in demand, it is not very meticulous to consider how to modify the design (in fact I think this is too much, I contact users It is very certain to propose a need, and finally due to the completely veto of this demand due to the user, it is, but what I face, the user I have no law, the user's demand users The user's official, SWEAT, does not change, the time can not be dragged, the reason is not detailed), directly from the beginning of the idea to write the target code first, then what kind of situation? The first thing is that time has already spent it (Oh, the order is certainly not much time, it is relatively said), and the development code will inevitably cause the purpose of writing tests to verify this code. Correct or not, in this case, don't say, ready to enter data, verify output data, how much is it? (I think it is the case, once it is determined to go, but still, when you come, you will plan it, there is no room to consider other things, don't say this. Things think about making another thing). In the case of test priority, the results and procedures I have written in an example very detailed, there is no time to write test, because now, there is nothing, what I want, I want to Let the test code are clear, simple, then can it be there?

Therefore, Test First is a good way to improve the correctness of test code. Here first, you will write the way the code is written. In fact, it is said that it is the third point I want to say, that test, really easy to test the code tested for test (memory, I used to do it before At the time, it is also tested for testing, huh, I don't know how other friends will be). Second, only write demand test (note, it is the target code demand, this code is of course yourself, ^ _ ^)

As mentioned earlier, to ensure the correctness of the test code, from the guarantee code itself, it is also a truth, but it is necessary to put it here, mainly because I write test, often It is easy to forget this, simple to say, when a test will involve multiple classes, it is actually very clear, unit test, you test your target class is OK, but everyone's understanding The level of writing test is different. It is more careful. When writing target class test, I want to be a class A, but I also take an assertion. Anyway, add more, in case of preventing case (too careful, in fact, I don't know). Don't look at a line of code, my feeling, more than 20 lines (empty line is not counted, SWEAT) one method is coming back after you write this method a few months later, the chances of halo are very large, at least immediately understand this method The internal structure is basically unable. There are few more accumulation, if the target class is associated with the five or more even more, more, I am afraid it is not one, two lines of code, and you will be careful, such as In a test case (I refer to the test case does not refer to the TestCase class, but a Test method in this class) Your assertion contains a test of one operation result of A, then in another test case If you still use this action, I am sure you will also write this assertion because your first care is not accidental. People say, be careful to make a thousand years of ship, but it is too worried. When writing tests, it is easy to make such a fault (唉, in fact, I am very easy to make such a fault, SWEAT).

Therefore, the responsibilities are clear, I only write the test you need, don't doubt this suspicion, careful, step by step. How do we use our code, then write such tests, now I have a test code as an example, write a public module, except for the document, it is recommended if I feel that I feel how to read the document, how to use the test code how to use the test code Or you simply copy my test code in the past.

Third, don't test it for testing (this sentence is when he chats with a friend, he said that he and Kent Back time Kent Back reminds him, here I don't have it to determine the correctness of this sentence, because Understand a sentence, the context is also critical, and I don't know much about my friend with the specific content and process of the Kent Back conversation, but here is still a point to talk about my own idea)

Don't test it for testing, ^ _ ^, although it is necessary to write test, everyone is developing, if your test code is to test, I still say that you are not "professional". (This sentence is a bit familiar, stars: Please, although everyone is Chinese, you want to copy me the word, still tell you to plagiarize), actually, I will listen to my friends, I don't understand, because after all, I just didn't have any head. The end of the end, I have heard it. But the detail, I don't think there is some truth (^ _ ^, I don't know if my idea is the intention of the Kent Back this sentence). The collapse is correct, the developer's test code is still not based on the test target code, so it can better ensure the correctness of the test code.

It is necessary to clarify that it is not to test the test code, huh, huh, the result of the test code (whether it is testing the first) is actually the same, it is possible to automatically (or semi-automatic, ^ _ ^) Test, I said, is the purpose of writing tests. Because of your purpose, the middle process is also very different. For developers, development code is the ultimate goal (what needs analysis, system design, etc. Not to meet the code of the user's needs, oh, of course, the most ultimate goat, 咱 rely on this supporting family, SWEAT, so the unit test code in the development process, it is just assist us to develop ( This seems to be said, SWEAT, don't lose my stinky eggs), so the ultimate goal of these test code is also to develop code, and it is the essence of the test code written by the test staff (the tester is to see me Article, 嘿嘿, sorry, really not writing).

Say the winding, but only clear this, then 2nd points will stand the feet, if the test code is started to test the target code, then Test First is a joke, even the goal There is not a possible test, and write test according to demand, no matter what the internal logic and boundary conditions, etc., etc., the tester must say that I am ignorant, can this also test?

So, to ensure the correctness of the test code, don't test it for testing, only if you don't test, you will truly do the first, second. (It seems that this should be put first, SWEAT, but you will fall again.)

Fourth, write a little (atom) test each time

This, in fact, it is white, it is a small incremental iteration in XP, and the test driver development emphasizes that although it is test priority, it is critical to testing the interaction of each other during the test development.

A very general test case, which tests this again, it is inevitable that it will complicate (the amount of accumulation of volume), so it is best to write a test case only for a need to write test, a small amount. Add a little test, run failure, modify the code, run the test, until success, then refactor the code, and finally test, is a self-iterative cycle of test driver development, and of course the control of the iterative period can vary from person to person.

The less tests each time you write, the less you concerned, the less demand, so the more concentrated, the less code of the relative test, the big problem can usually be split into multiple small The problem is solved separately. If the test code is too complicated, it can naturally reduce its complexity by splitting complex test code to multiple small relatively simple test code segments. Five, Clean Code That Works, (of course, including test code, ^ _ ^)

A good design, the code that can't be confusing, the simplest, there is no good indentation format, looks really a nightmare, now the tool is so good, but I can still see a lot of work in my work. Formatted code, SIGH, minimum requirements can't do it, don't say let him reactivate the code, it is estimated that writing tests are not playing, Test First is more free.

So don't look at this sentence, you can understand it again. (Here is Test First First First, as mentioned earlier, I am actually writing the test after the development of the code, I have been hesitant every time, is it first write test, and many times I can't hold my own head. The design that has been "shaped" is to write the development code, test, always think that there is another way, but after I press the test, I feel completely different, Test First is not originally I want to think. It is simple, so although I have said a bunch of its benefits, I hope that friends who have not experienced don't have so much tube. Try first, you will like it, but if you want to use it everywhere, or have to Improve the level of writing test code, because the test code is not so simple, especially after the relationship with the database and the web).

Ok, this is, in fact, I don't have to say any more. In fact, everyone is eating a number. As for how to do, hey, look at "Test Drive Development", maybe there will be, and I will exceed the category here, about this, I will hit this, because the master's Golden jade words are more convincing. Force, I don't want to pay here.

Sixth, for an application framework, it is best to write a test framework for this frame (this is actually a very specific content, but now Java is much more used in the web, the test is relatively difficult, so there is this point)

This is to apply it, ^ _ ^, in fact, maybe this is something that friends want to exchange, because I started to learn to write test, I don't want to write, but I don't know how to write, right A complex application, such as front-end web, background database, just associate with these two, and then simply write testing is also a big pile of code, and some don't know how to test. I am still afraid of testing of database ends, I hope that there is a friend to discuss it.

Here, my point of view is clear, an application framework, to support the test framework for this framework to make the test code written clearly to ensure its correctness. The purpose of the test framework is to ensure the correct construction of the test environment. Here you talk out the topic, the test environment, I think it is the key to the test, and it is also the hardest aspect of the unit test. For an interface test (pure interface, not an implementation class), you may need to write an implementation class that extends this interface to assist the test (I will take the example, the first thing to consider using Decorator is first after considering this mode) Interface test, so that I can throw this mode when testing the specific Decorator implementation and test the class separately, and finally when the Column node is processed with Builder mode, it is actually the default Builder to do this test), so It is the second point I said; the application framework is usually the interface architecture, abstract application interface, and the specific application implements functions, such as the common web framework structs, which is the interface, To do a good job in it, the test of the frame itself is essential, but in particular, it is actually in the framework, as long as it makes the Action that meets the requirements, this, the test of the Action is the general application It is necessary to consider. For the test of Action, a simple way is to isolate your test code and Structs framework in the action, so you don't need to access the Structs framework, directly test, but this is not complete (but doing this is also good, that is, The logic code is forced to write other places, not written in the action, of course, the Action itself is not used to write these, but I see, many programmers are actually used to write business logic code directly in Action, this way The advantage is very small, it is to write a few lines of code in front of you, but it will not be seen in the future. This is my seventh point, the difference between the big lazy and the little lazy. The best situation is completely testing the Action class, from calling it Start, but this way, ready to test the environment will be very troublesome, preparing an HTTP Request object is not casual, will make people hope, if only one, two such tests will be, it actually Thousands, so this time needs a test frame (huh, suddenly, start to tell the test framework, there may be misunderstandings, saying is the code of the test application framework itself, which is of course not less, but the test said here The framework is a framework for the TestCase to be written when the application framework is developed. Say it is higher than the assembly), you want to write a test framework for the Structs framework (now open source frame), so that the construction of the test environment simplifies to the least, so that you can only pay attention to the requirements part of the test section, The test code itself does not look so huge. Seven, lazy is a common problem of programmers, but don't have a lazy. (I have this point of view: the level of programmers is high, in fact, from him to be lazy ... ^ _ ^)

In the sixth point, it has already brought this topic. In fact, it is just that you think about your thoughts, it can't be very formal, because this nature and test have no relationship. I am too lazy, or else you will not choose this profession, because the program can help me do things, so I can just look back, ^ _ ^.

But there are many kinds of lazy, there is a kind of direct, only lazy, or not format the code, it is very casual, it seems to make you have a lot less, but let people think, let the people When you look at it (especially when you check the error), pay a higher price, I think, whether you are looking at this code next time, it is more expensive to spend more time, but not A menu is automatically formatted with less code. Designed for changes, it is a more lazy future. There is a point of view, now I can't think of it, it seems that there is ("Reconstruction", XP, test driver development, etc.), just add new features to an existing module. First reforige the existing code, so that the code is suitable for adding this new feature (it seems that this should be a "reconstruction" book), and the reconstruction is not to say to make the code better, its purpose is to Adapt to change, the change is very large, but can improve existing code by reconstruction, so that the existing code is suitable for new changes, then the complexity of changes is reduced.

So I said that laziness is free, don't affect the overall situation, the code is clear, and the necessary reconstruction is not these just small laziness, they are paying more than the future. Test code, just code.

Ok, finally finished, there is a bit of a few mini points in the middle (Clean Code That Works seems to cover all), or summed up, thinking about it, or this sentence: Clean Code That Works. The master is incisive. However, think about it, what kind of point saying it is, it seems to be what to do, just say that these points can greatly guarantee the correctness of the test code, but these key points, how to do it , I want everyone to see a book, "test driver development", "Reconstruction" and "extreme programming - hug changes" These are rare good books, I think, relatively other books, this 3 key and It is not that it has a very deep theory or technology, but they all guide the programmer practice, how to start programs from the small place, develop a good programming habit, and more like these masters do hand to teach the program. It is rare that the writing is still very good, and it is easy to understand.

The first time I wrote such a long thing in 9CBS, as a "handling", because I wrote something on the Internet, I still have a hurry (although I have read it twice, I have made some modifications), and I have inevitable (especially writing writing) I will always have a bit, blush, and write articles, it is really difficult to test first, it is difficult to guarantee quality, welcome advice, and hope to discuss each other.

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

New Post(0)