為什么測試喜歡ie
by Evelyn Chan
通過伊芙琳·陳
為什么我現在喜歡測試,以及為什么您也應該如此。 (Why I now appreciate testing, and why you should, too.)
There’s a common misconception that writing tests slows down development speed. While the benefits of testing may not be immediately noticeable sometimes, it’s been my experience thus far that testing allows me to work faster in the long run, even on a small team.
有一個普遍的誤解,認為編寫測試會降低開發速度。 盡管有時有時無法立即注意到測試的好處,但到目前為止,根據我的經驗,即使在一個小型團隊中,測試也可以使我從長遠來看可以更快地工作。
After implementing tests for a full-stack application, I’ve come to appreciate how useful it is to effectively test your apps and how it influences your coding ability.
在為全棧應用程序實施測試之后,我開始意識到有效測試應用程序的有用性以及它如何影響您的編碼能力。
快速介紹一下測試堆棧的外觀 (Quick intro to what a test stack looks like)
Jest is a testing library developed by Facebook that comes prepackaged with a bunch of cool methods to make testing easier. On a recent project, my team and I chose Jest because of its ease of use and its wide range of built-in functions that help streamline your testing. It’s very simple to set up and uses the Expect library for assertions.
Jest是由Facebook開發的測試庫,它預包裝了許多很酷的方法,以簡化測試。 在最近的項目中,我和我的團隊選擇了Jest,因為它的易用性和廣泛的內置功能有助于簡化測試。 設置和使用Expect庫進行斷言非常簡單。
Enzyme is the de-facto way of testing your React app. It’s pretty magical in that it renders your React components within Jest, allowing you to test your front end JSX code effectively without manually transpiling it. You render your components using one of three methods: shallow, mount, or render.
酶是測試您的React應用程序的實際方法。 這非常神奇,因為它在Jest中呈現了您的React組件,使您無需手動轉譯就可以有效地測試前端JSX代碼。 您可以使用以下三種方法之一來渲染組件:淺,安裝或渲染。
SuperTest allows you to make API calls without actually making the call, by intercepting it. API calls can be expensive and/or slow for unit tests, so you won’t want to make the call and wait for the response. Otherwise, it’ll slow down your tests and take forever to run.
SuperTest通過攔截來允許您進行API調用而無需實際進行調用。 API調用對于單元測試而言可能既昂貴又緩慢,因此您不希望進行調用并等待響應。 否則,它將減慢您的測試速度,并導致永遠運行。
這是我學到的 (Here’s what I’ve learned)
測試可防止回歸 (Testing prevents regression)
Adding new features frequently breaks existing code, and having tests can prevent this from happening. Testing helps ensure that your code works as you intend it to. As simple as it may sound, that makes a world of difference.
添加新功能經常會破壞現有代碼,并且進行測試可以防止這種情況的發生。 測試有助于確保您的代碼按預期工作。 聽起來似乎很簡單,但卻與眾不同。
It may seem silly to hear that not all programmers can articulate the code they write, but it’s actually pretty common. How many times have you been asked to explain something you wrote on the fly under a strict deadline and found yourself stuttering to answer? Writing tests forces you to think clearly about what exactly your function is taking in as an argument and returning as an output.
聽到并非所有程序員都能清楚地表達他們編寫的代碼,這似乎很愚蠢,但這實際上很常見。 在要求您嚴格解釋最后期限的情況下,有多少次您要解釋自己寫的東西,卻發現自己口吃不住? 編寫測試會迫使您仔細考慮您的函數究竟將什么作為參數并作為輸出返回。
Even if you do understand the purpose of each line of code, as time goes on, you’ll inevitably start to forget. Tests provide an important complement to documentation that helps you move back and forth between code bases quickly. Taking the time to write good, efficient tests allows you to refactor your code much more easily and develop with confidence.
即使您確實了解每一行代碼的目的,隨著時間的流逝,您也不可避免地會忘記。 測試是對文檔的重要補充,可幫助您快速在代碼庫之間來回移動。 花時間編寫良好,高效的測試可以使您更輕松地重構代碼并充滿信心地進行開發。
使用模擬和存根進行單元測試。 (Unit test with mocks and stubs.)
In a unit test, you test a specific piece of code one at a time. In any application, your code probably will make calls to and depend on other classes or modules. As these relationships between classes increase in complexity, it’ll obfuscate the source of bugs.
在單元測試中,您一次測試一個特定的代碼段。 在任何應用程序中,您的代碼可能都會調用并依賴于其他類或模塊。 隨著類之間的這些關系增加復雜性,它將掩蓋錯誤的來源。
In order to isolate and debug effectively, you can replace these dependencies with a mock or a stub to control the behavior or output you expect.
為了有效地隔離和調試,可以使用模擬或存根替換這些依賴關系,以控制所需的行為或輸出。
For example, let’s say you want to test the following method:
例如,假設您要測試以下方法:
import database from 'Database';import cache from 'Cache';
const getData = (request) => { if (cache.check(request.id)) { // check if data exists in cache return cache.get(request.id); // get from cache } return database.get(request.id); // get from database};
Stub:
存根:
test('should get from cache on cache miss', (done) => { const request = { id: 10 }; cache.check = jest.fn(() => false);
getData(request); expect(database.get).toHaveBeenCalledWith(request.id); done();});
Mock:
嘲笑:
test('should check cache and return database data if cache data is not there', (done) => { let request = { id: 10 }; let dummyData = { id: 10, name: 'Foo' } let cache = jest.mock('Cache'); let database = jest.mock('Database'); cache.check = jest.fn(() => false); database.get = jest.fn(() => dummyData);
expect(getData(request)).toBe(dummyData); expect(cache.check).toHaveBeenCalledWith(request.id); expect(database.get).toHaveBeenCalledWith(request.id); done();});
The main difference between the two lies in state vs behavioral manipulation.
兩者之間的主要區別在于狀態與行為操縱。
When using mocks, you replace the entire module with a mock object. A stub is a forced output of a function no matter the given input. Mocks are used to test if a function is being called with the right arguments, and stubs are used to test how a function operates on a given response. Stubs are used to validate the state of a method, whereas mocks are used to evaluate the behavior.
使用模擬時,可以用模擬對象替換整個模塊。 無論給定輸入如何,樁都是函數的強制輸出。 模擬用于測試是否使用正確的參數調用了函數,而存根用于測試函數如何在給定的響應下運行。 存根用于驗證方法的狀態,而模擬用于評估行為。
Jest provides jest.fn
, which has both basic mocking and stubbing functionality. A Jest mock can also stub method outputs, and in this case be both a mock and a stub.
Jest提供了jest.fn
,它具有基本的jest.fn
和存根功能。 Jest模擬還可以對方法輸出進行存根處理,在這種情況下,它既可以是模擬也可以是存根。
The concept of mocks in testing and how they differ from stubs can get pretty complex, so for a deeper dive, check out the links at the end!
測試中的模擬概念以及它們與存根的區別會變得非常復雜,因此,如果要進行更深入的研究,請查看最后的鏈接!
知道您不需要測試的內容。 (Know what you don’t need to test.)
You’ll want to test every method you write. But keep in mind that depending on your code base, 100% test coverage is unlikely and most times not even necessary.
您將要測試您編寫的每種方法。 但是請記住,根據您的代碼庫,不可能100%覆蓋測試,大多數情況下甚至沒有必要。
With Jest, you can easily track your test coverage by adding a--coverage
tag to your test script on your CLI. While this is a useful measure, take this with a grain of salt — the way Jest measures test coverage is through tracing the call stack, so a higher test coverage doesn’t necessarily mean your tests are effective.
使用Jest,您可以在CLI的測試腳本中添加--coverage
標記,從而輕松跟蹤測試范圍。 盡管這是一個有用的措施,但要花一點點時間-Jest衡量測試覆蓋率的方法是通過跟蹤調用堆棧,因此更高的測試覆蓋率并不一定意味著您的測試有效。
For example, in a previous project, I used a library to implement a carousel component. Within the component was a function to render a list based on an array. To increase test coverage, I wrote a test to count and compare the number of rendered items to the array. The carousel component modified the number of items being rendered on the DOM to be more than a 1:1 output, even though the implementation visually displayed the correct number of elements in the browser. I chose to forego the test coverage because it was essentially testing the carousel library instead of my code.
例如,在上一個項目中,我使用一個庫來實現輪播組件。 組件內有一個基于數組呈現列表的函數。 為了增加測試覆蓋率,我編寫了一個測試以計算并比較渲染項目到數組的數量。 輪播組件將DOM上呈現的項目數修改為大于1:1的輸出,即使該實現在瀏覽器中直觀地顯示了正確數量的元素。 我選擇放棄測試范圍,因為它實際上是在測試輪播庫而不是我的代碼。
Let’s assume a Listings
component with a method renderCarousel
that renders a carousel from an external library:
讓我們假設一個Listings
組件帶有一個renderCarousel
方法,該方法從外部庫中呈現一個輪播:
Ineffective test:
測試無效:
test('should return the same number of elements as the array', (done) => { // Full DOM render let mountWrapper = mount(<Listings />);
// State change to trigger re-render mountWrapper.instance().setState({ listings: [listing, listing, listing] });
// Updates the wrapper based on new state mountWrapper.update();
expect(mountWrapper.find('li').length).toBe(3); done(); })
Effective test:
有效測試:
test('should call renderCarousel method when state is updated', (done) => { // Mock function inside component to track calls wrapper.instance().renderCarousel = jest.fn();
// State change to trigger re-render wrapper.instance().setState({ listings: [listing, listing, listing] });
expect(wrapper.instance().renderCarousel).toHaveBeenCalled(); done(); });
The difference between the two lies in what the tests are actually testing.
兩者之間的區別在于測試實際測試的內容。
The first example evaluates the renderCarousel function, which calls the external library. The second test evaluates whether the renderCarousel is simply being called. Since the external libraries are somewhat akin to a black box of magic and are being tested by their own developers, it’s not necessary to write a test that makes sure it’s working correctly.
第一個示例評估renderCarousel函數,該函數調用外部庫。 第二個測試評估是否只是簡單地調用renderCarousel。 由于外部庫有點類似于魔術的黑盒子,并且正在由其自己的開發人員進行測試,因此無需編寫確保其正常工作的測試。
In this scenario, we only need to test that the library is being called and have faith that the library’s developers are handling the testing.
在這種情況下,我們只需要測試正在調用該庫,并確信該庫的開發人員正在處理測試。
Understanding what you do and don’t need to test lets you maximize your time to remove redundancies.
了解您要做的事情和不需要進行的測試,可以使您最大限度地利用時間來消除冗余。
精心設計的測試會導致精心設計的代碼。 (Well-designed tests lead to well-designed code.)
Designing your code knowing you’ll have to write tests for it improves your code. This is known as test-driven development, which is supported by a wide margin of the coding community.
在設計代碼時就知道您必須為此編寫測試,從而可以改善代碼。 這就是所謂的測試驅動開發,它得到了編碼社區的廣泛支持。
In order to appropriately unit test functions, you need to strip away logic and test one method at a time. This structure forces you to write modular code and abstract logic away from your components.
為了適當地對測試功能進行單元化,您需要剝離邏輯并一次測試一種方法。 這種結構迫使您編寫遠離組件的模塊化代碼和抽象邏輯。
As you think about your code in terms of writing tests, you’ll start to develop the habit of decoupling your code. I’ve discovered that writing your code this way seems to produce a story-like structure that’s both easier to write and easier to follow.
在考慮編寫測試代碼時,您將開始養成將代碼分離的習慣。 我發現以這種方式編寫代碼似乎產生了一個類似故事的結構,既易于編寫,又易于遵循。
Let’s consider a scenario where we’re calling an endpoint to fetch data from a database and formatting that data before we return it.
讓我們考慮一個場景,在該場景中,我們調用端點從數據庫中獲取數據并在返回數據之前對其進行格式化。
Too much logic:
邏輯太多:
// Define endpointapp.get('/project', (request, response) => { let { id } = request.params; let query = `SELECT * FROM database WHERE id = ${id}`; database.query(query) .then((result) => { const results = []; for (let index = 0; index < data.length; index++) { let result = {}; result.newKey = data[index].oldKey; results.push(result); } response.send(results); }) .catch((error) => { response.send(error); }) })
Easier to test:
更容易測試:
// Make call to database for dataconst getData = (request) => { return new Promise((resolve, reject) => { let { id } = request.params; let query = `SELECT * FROM database WHERE id = ${id}`; database.query(query) .then(results => resolve(results)) .catch(error => reject(error)); };}
// Format data to send backconst formatData = (data) => { const results = []; for (let index = 0; index < data.length; index++) { let result = {}; result.newKey = data[index].oldKey; results.push(result); } return results;}
// Send back dataconst handleRequest = (request, response) => { getData(request) .then((result) => { let formattedResults = formatData(result) response.send(formattedResults); .catch((error) => { response.send(error);}
// Define endpointapp.get('/project', handleRequest);
While the second example is longer, it’s much easier to follow along. The logic is clearly abstracted and isolated, making it much easier to test.
盡管第二個示例更長,但更容易理解。 邏輯被清楚地抽象和隔離,使其更容易測試。
If you’re just starting out with testing/coding, it can be hard to discern what makes a well-designed test. You can’t write effective tests if your code isn’t designed well, but you can’t figure out what makes code suitable for the tests you can’t write! Fortunately, this leads to my last tip…
如果您只是從測試/編碼開始,那么很難分辨出什么是設計良好的測試。 如果您的代碼設計不當,您將無法編寫有效的測試,但是您無法弄清楚是什么使代碼適合您無法編寫的測試! 幸運的是,這引出了我的最后一條提示……
在編寫代碼時編寫測試 (Write tests as you code)
The best way to incorporate testing is to write it alongside your code. Otherwise, it’ll feel overwhelming when you write them all at once and have to go back and forth between all your test and method files.
合并測試的最佳方法是將其與代碼一起編寫。 否則,當您一次全部編寫它們并且不得不在所有測試文件和方法文件之間來回切換時,會感到不知所措。
Many beginners make the mistake of treating tests as something you do after all your code is written. If you treat it as a task you do alongside your code, it not only improves your code but also makes writing them more achievable.
在編寫所有代碼之后,許多初學者會犯錯誤,將測試視為您要做的事情。 如果您將其視為與代碼一起執行的任務,那么它不僅可以改善代碼,還可以更輕松地編寫代碼。
In a system with cleanly abstracted APIs, you can test each class before moving on so you know which part of the logic is broken. For instance, my ‘get’ endpoint calls getData to interact with the database. I would write tests for getData first and make sure those are green. That way, I know that if any of my controller tests fail, it probably has to do with the way I’m calling getData.
在具有干凈抽象的API的系統中,您可以在繼續進行之前測試每個類,以便知道邏輯的哪一部分壞了。 例如,我的“ get”端點調用getData與數據庫進行交互。 我會先為getData編寫測試,并確保它們是綠色的。 這樣,我知道如果我的任何控制器測試失敗,則可能與我調用getData的方式有關。
Hopefully this write-up has helped you understand why testing is so useful and has equipped you with some tips on how to get started. This only hits the tip of the iceberg with testing, though! Here are some resources if you want to learn more:
希望這篇文章可以幫助您了解測試為什么如此有用,并為您提供了一些入門的技巧。 但是,這只是通過測試擊中了冰山一角! 如果您想了解更多,這里有一些資源:
https://martinfowler.com/articles/mocksArentStubs.html
https://martinfowler.com/articles/mocksArentStubs.html
https://medium.com/@rickhanlonii/understanding-jest-mocks-f0046c68e53c
https://medium.com/@rickhanlonii/understanding-jest-mocks-f0046c68e53c
If you enjoyed this article, please click on the ?? and share to help others find it. Thanks for reading!
如果您喜歡這篇文章,請單擊??。 并分享以幫助他人找到它。 謝謝閱讀!
翻譯自: https://www.freecodecamp.org/news/why-i-now-appreciate-testing-and-why-you-should-too-74d48c67ab72/
為什么測試喜歡ie