Nodejs learning notes test drive: nodejs learning notes Test
Share Chapter 2 about the test driver. The tests here are mainly aimed at Web backend tests-Why do you need to write test cases (that is, whether test cases are time-consuming) and how to improve your test cases, code Design: how to simplify the writing of test cases and some ideas in the future.
1. Why do you want to write test cases?
This habit is generally considered to be a behavior that delays the development progress. You need to spend almost the same time as the development code to gradually improve your test cases. However, during the development process, if you take responsibility after developing a piece of code, instead of simply handing the problem to the tester for discovery, you will usually perform some manual tests at this time. For example:
Execute some methods in the code to check whether the output value meets expectations.
Modify the database/cache, and then execute some methods to check whether the database changes meet expectations.
Use a tool to simulate some interfaces and check whether the return value of the interface/the change value of the database meets expectations.
If there is a front-end page, it will also involve joint debugging of the front-end, that is, you need to use front-end interaction on the front-end page to check whether the front-end feedback meets expectations, to indirectly verify the correctness of the back-end code.
Modern testing tools are trying to abstract these manual testing behaviors into code blocks as much as possible. When you consciously perform manual testing, you are actually starting to try the testing cases. Since the test can be performed manually, why do I need code to perform the test?
Code can be reused or implemented after simple refactoring. However, when you choose manual, you need to start over each time.
A mature workflow should include a code review process. There are many methods to review the code. Read your code one by one, check the completeness and correctness of your test code, and then run your test case. The latter is simpler.
When code changes, such as fixing bugs, it is difficult to ensure that your changes affect other parts that depend on your code. In the era of manual testing, there is a regression test, that is, when you fix a Bug, retest your system. But if you already have a complete test case, execute the command directly.
Same as above when you refactor the code.
2. How to improve your test cases
Before entering the perfection stage, let's talk about how you can implement the test cases.
describe Meme do before do @meme = Meme.new end describe "when asked about cheeseburgers" do it "must respond positively" do @meme.i_can_has_cheezburger?.must_equal "OHAI!" end end describe "when asked about blending possibilities" do it "won't say no" do @meme.will_it_blend?.wont_match /^no/i end endend
The above Code comes from Ruby's minitest. The code block contained in before is required before the following test cases are executed. Generally, a corresponding method is supported to complete the execution of the test cases. Make minor judgments in each case.
The first section mentions some testing content that is often involved in manual testing. Here we will describe 2 and 3. During database-related tests, you need to insert a test data in before and delete the test data in after. In the intermediate test case, after executing the corresponding method, check the data changes/check whether there are expected exceptions/whether the expected results are returned to confirm the correctness of the Code. If it is an interface, it initiates a corresponding request through code, and then checks whether the returned content returns expectations. If necessary, it checks whether the data in the database meets the expected changes.
Now you have a test case, but you still need to consider a special situation. Now I have written a relatively complete test case for a function. After all the tests are completed, I found that the function error still exists in the online log. Check and find that a branch of the function was not tested before the test. In some cases, the Branch runs online. The result is a very obvious syntax error and an error is reported, is there a way to ensure that all the code has been tested? Here we need to introduce a concept called test case coverage rate. Basically, each language has a response implementation. The test case coverage rate quantitatively tells you whether your test case has run all the code in the XX file. What you need to do is to ensure that your coverage rate is kept at 100% as much as possible.
In a sense, test cases and test coverage are used to improve developers' self-confidence in their code. However, they are not omnipotent. The possibility of some parameters may always be missed in the test case. Of course, your code does not write code for this possibility, the final test case coverage rate can only tell you that the code you write has helped you detect the test. It is powerless if you do not consider the possibility. Therefore, write strict code as much as possible. For example, use = instead of = in javascript, and use strong programming specifications, this reduces the potential risk caused by a large range of accepted parameters.
3. Code design how to simplify the writing of Test Cases
The entire Web (not limited to web) usually includes three layers of Code-data processing and operations alone, databases, and specific network protocols. Specifically, simple data operations are mainly used to process common operation functions or other code. in the traditional sense, databases are involved in M in MVC, and the specific network protocol is the corresponding C. The three tests correspond to the first three of the general test content in section 1.
Because page rendering and protocol simulation are usually involved in the C layer, the focus of testing is usually placed in functions and database-related code, which can reduce the complexity of the test case code, this requires the Controller to have as few code as possible. Some current suggestions for applications with high complexity:
The basic validation of data is put on the M layer. If Ruby is used for development, ActiveRecord and validation ID provide a convenient validation function.
In the code, we try to use the Pub/Sub mode in combination with the hooks provided by some ORM to implement communication between models. For example, if A publishes A message when it is created, B changes its own attribute value after listening to the message.
Use the Command mode to extract business-independent functions from the system, such as sending emails.
For more information, see Laravel wisper resque.
4. Conception
The above content avoids the test cases that require joint debugging on the frontend and backend. The following content focuses on this part. Ruby has some elegant implementations in this direction. If you are interested, you can directly enjoy Capybara.
With the popularization of a series of browser drivers, including Selenium Phantomjs and Watir based on the former, using code to control browsers is no longer a complicated task. Based on this capability, we can try to divide the frontend-based test into four steps:
Wait for a symbolic element to appear (for example, wait for the page to load or a content to load asynchronously)
Simulate user operations. The operations here include and are not limited to user clicks and user input.
Wait for the symbolic element to appear in the feedback (for example, in a XX input box)
Determine whether the content meets expectations
Based on this process, most front-end tests can be implemented. However, simply relying on this process is not enough, because obstacles such as the verification code may appear on the page. without modifying the code, you can try to get the content through the database/cache. Similarly, similar to the test interface, it also involves inserting test data in the database before the test, serious data changes in the database after the test case is executed, and deleting the test data after all tests are completed. Eventually, the implementation of this test case code requires a certain understanding of the front-end and backend. Currently, more general solutions are also designed based on capyloud.
Finally, paste the Capybara code to end the content:
feature "Signing in" do background do User.make(:email => 'user@example.com', :password => 'caplin') end scenario "Signing in with correct credentials" do visit '/sessions/new' within("#session") do fill_in 'Email', :with => 'user@example.com' fill_in 'Password', :with => 'caplin' end click_button 'Sign in' expect(page).to have_content 'Success' end given(:other_user) { User.make(:email => 'other@example.com', :password => 'rous') } scenario "Signing in as another user" do visit '/sessions/new' within("#session") do fill_in 'Email', :with => other_user.email fill_in 'Password', :with => other_user.password end click_button 'Sign in' expect(page).to have_content 'Invalid email or password' endend