ویرگول
ورودثبت نام
عرفان ندیمی
عرفان ندیمی
عرفان ندیمی
عرفان ندیمی
خواندن ۱۲ دقیقه·۵ سال پیش

Building single_page apps

+ minutes. In CI with our beefy build machines we’re now able to run the entire Jest suite in 4 minutes 30 seconds.





Gary Borto


Come check out our new digs at 999 Brannan, our second office in San Francisco!


We’d been using Mocha at Airbnb since September 2013, but due to increasing growing pains, we’ve recently migrated from Mocha to Jest. The migration actually turned out to require minimal changes to our tests and infrastructure, and provided a myriad of benefits.





Comparison of Mocha vs Jest, testing that 1 + 2 === 3


Minimal Changes to Tests


We’ve put significant effort into ensuring that our tests follow best practices and a fairly strict coding style. This has helped us to mitigate flakiness and enable cross team collaboration, because tests look the same across different sections of our code. It was pretty important to us that our test files look more or less the same before/after the migration.


Most teams will not have to change the contents of their test files. In fact the below snippet was enough at Airbnb to paper over the differences between our usage of Mocha and Jest.


This is possible because the Jest API and the Mocha API are similar with only slightly different function names. Which functions you need to control for (or change in your tests) depends greatly on which functions you’re using. We also had a few test calls, which were 1:1 replaceable with it which is what we’ve decided as standard. Below is a quick example of what our tests look like before and after our migration to Jest.


Simplified Testing Architecture


We initially rolled out code coverage using Istanbul and Mocha in January 2016, but we discovered that instrumenting our source files was expensive, and added an unreasonable amount of time to our tests.


To solve this problem we wrote some custom logic to batch our tests into chunks, run them in separate processes, collect coverage on each, and then merge the coverage into a single report at the end.


Coverage was collected (and enforced) in a different CI job than our test suite. The test suite itself was later parallelized by dispatching chunks of the test suite to different worker machines and aggregating the results at the end.


Jest automatically handles splitting tests across processes, collecting and aggregating coverage. This was a major perk for us. By utilizing this fact we were able to remove our custom logic in both jobs, and rely solely on Jest to handle this intelligently for us.


Improved Performance


For projects with many test files, Jest will get you improved performance out of the box. It’s able to do this through a couple of mechanisms.


Parallelization. This is probably what Jest gets the most attention for, and for good reason. If you aren’t already parallelizing your CPU-bound work, you can expect to see a large performance gain by doing so.


As stated above we were already parallelizing our tests at Airbnb, but we were doing so by getting a list of all our test files and dividing them equally among our workers. This left opportunity for a worker to get either an abnormally fast or abnormally slow queue of tests, and led to wasted CPU cycles. Jest instead takes a round-robin approach, and runs your slowest tests first, helping you to squeeze the most out of your processing power.


It has a built-in babel transform cache. Applying transforms to code is very CPU intensive. By utilizing a cache that is shared across processes, you can dedicate your CPU to running your code and cut a lot of time off your runs.


With Mocha our suite took ~45 minutes to run locally, and sometimes it wouldn’t complete at all due to the memory pressure of running our full suite in a single thread. With Jest it’s down to 14.5 minutes locally. We saw a similar improvement on our build server with Mocha clocking it at 12+ minutes (after our work to parallelize across machines) and Jest finishing in 4.5 minutes.


Reducing Flakiness


When you’ve got a test suite as large as ours (several thousand test files), running your tests in a single thread will inevi


tably lead to flakiness. When we started working on the migration, roughly 12% of our builds would need to be rerun due to flake, and we had tests in our suite that required other tests to run first or they wouldn’t pass.


Being run in isolation means that it is impossible for a test to fail due to side effects of other test files in your suite. This is especially helpful for errors that are thrown in setTimeout calls that happen after a test has completed. Now it’s much easier for us to investigate a flaky test by checking the test and source file for any asynchronous code.


After migrating to Jest and fixing the tests that failed in isolation we were able to reduce our flake rate to ~1%. This saves our developers hours of time per work day as they no longer have to wait for a build to fail, and repeatedly rerun the test suite until it passes. Additionally on the rare occasions that flake does happen, we’re able to more accurately identify where it is coming from. It’s easier to identify, because of file runs in its own process, so it is guaranteed that flakiness is coming from within that file. With Mocha, a bad timer in file x, could cause a test to fail in file y.


If you’re dedicated to reducing flakiness, you can take this a step further and reduce flake in your tests even more by killing timers that are set in your tests:


Taking Performance a Step Further


Jest was much faster for us out of the box, but initially we weren’t seeing the sort of improvement that we expected. After profiling a few runs, we found that our global spec_helper.js file was the culprit. This is a file that we set up with Mocha to configure some global helpers that made our tests more convenient to write. For example, we use Enzyme for testing our React code, and to make writing tests easier, we include chai-enzyme. Rather than having all of our developers hook up this library manually in every test file, we hook it up in spec_helper.js which is run before all of our tests.


This turns out to be really problematic in Jest. Because each test file is run in a clean virtual machine, Jest reruns the spec_helper.js file once for each test file. In the case of the above example, importing chai-enzyme starts a chain that imports all of enzyme, which then imports all of React and ReactDOM. This takes 480ms even for tests that do not include React. In our case 480ms * several thousand files meant that we were spending over a minute just setting up this library. With Mocha, we didn’t feel the pain from this because it isn’t parallelized, and only runs the spec_helper.js file one time.


To get around this, we got a little bit creative with Jest’s mocking capabilities. By utilizing the callback on jest.mock() we were able to intercept enzyme imports, and load chai-enzyme only for tests that need it.


Developer Sentiment


Our ultimate goal with this migration was to improve our developer experience, both when writing their tests and when running them. We’ve only been on Jest a few weeks, but so far we’ve seen nothing but positive feedback:


Just wanted to come in here and give a big ++ to anyone who worked on the Jest implementation. The amount of dev time it saves is uncanny. Like, what used to take 3 hours to write now takes like 30 minutes. No joke.


We are always looking for talented, curious people to join the team. Or, if you just want to talk shop, hit me up on twitter any time @garyborton.

ساختن صفحه ی برنامه های

ارائه برنامه های تک صفحه ای به عنوان بخشی از دو هفته نامه سری بحث فنی در Airbnb HQ در سانفرانسیسکو. در اینجا پیوندهایی به اسلایدها و ویدئویی از ارائه کامل و جلسه پرسش و پاسخ وجود دارد:


ویدئو در YouTube | اسلایدها در SlideShare


تشکر از تمامی کسانیکه حضور داشتند! ما خانه ای مملو از حدود 150 مهندس داشتیم که با مجوز از تاکوهای فانتزی و آبجو ، با مجوز از Chebn Sam Airbnb گرم شدند.


ساخت لیست های دلخواه با استفاده از Backbone.Js


این سخنرانی شامل چندین نکته و ترفند خاص در مورد Backbone.Js ، Handlebars و Rails بود که تیم برای ساخت Wish Lists استفاده کرد ، که ما در ماه ژوئن راه اندازی کردیم. من به نحوه راه اندازی مجدد برنامه از Rails به Backbone ، استراتژی بارگیری تنبل داده ها ، نحوه نمایش Backbone به صورت مدولار و DRY ، نحوه مدیریت اثر انگشت CDN و دارایی هنگام تولید HTML سمت مشتری و نحوه برخورد ما با I18n اشاره می کنم. مشتری ، از جمله چیزهای دیگر.


Polyglot.Js


یکی از این نمونه هایی که بیشترین واکنش را به همراه داشت کتابخانه Airbnb’s homegrown JavaScript I18n بود که ما به تازگی آن را با نام Polyglot.Js باز کردیم. Polyglot یک ابزار کوچک است که استفاده از عبارات ترجمه شده را هم در مرورگر و هم در Node آسان می کند. آن را بررسی کنید و نظر خود را با ما در میان بگذارید!


آینده برنامه های تک صفحه ای


در این سخنرانی من همچنین معنی ساخت یک برنامه تک صفحه ای را معرفی کردم و دو رویکرد مختلف را بیان کردم: راه آسان و راه سخت.


راه آسان








Easy Way نام من برای برنامه های تک صفحه ای است که تقریباً به طور کامل در مرورگر اجرا می شوند. مسیریابی ، نمایش رندر ، لایه مدل ، ماندگاری ، I18n ، قالب بندی ارز و ... همه در مشتری اجرا می شوند. این برنامه بصورت آزاد با سرور که بیشتر فایلهای استاتیک را سرویس می کند و برخی از RESTful API ، که ممکن است در همان سرور در حال اجرا باشد یا نباشد ، جفت می شود. این روش با کمک کتابخانه ها و چارچوب هایی مانند Backbone.Js و Ember.Js در حال محبوبیت است. شما می توانید به صورت آنلاین منابع و آموزش های زیادی را در مورد نحوه طراحی این برنامه های سمت مشتری پیدا کنید.


دو روش اصلی در راه آسان وجود دارد: عملکرد و سئو. ضربه عملکرد از آنجا ناشی می شود که قبل از اینکه برنامه بتواند HTML قابل توجهی را ارائه دهد ، مرورگر باید همه اسکریپت های خارجی و درون صفحه را بارگیری و ارزیابی کند. توییتر چند ماه پیش یک پست وبلاگی عالی در این باره نوشت. آنها رندر سمت کلاینت را با رندر سمت سرور جایگزین کردند و "زمان اولین توییت" را با 5 برابر کاهش یافتند. این عظیم است


SEO برای یک برنامه با محتوای سنگین مانند Airbnb بسیار مورد توجه است. ما می خواهیم یک تجربه جستجو و تعاملی غنی و تعاملی ایجاد کنیم اما کاملاً باید اجازه دهیم موتورهای جستجو ما را خزنده کنند. انجام هر دو کار سخت است ؛ ما چند روش برای به اشتراک گذاری ارائه نمایش بین JavaScript در مرورگر و Rails در سرور امتحان کرده ایم و نتایج در بهترین حالت ناخوشایند بوده است.


راه سخت








در مقابل ، Hard Way همان چیزی است که من آن را webapp می نامم و می تواند به طور یکپارچه هم روی cilent و هم سرور اجرا شود. منطق برنامه و ارائه نمایش می تواند در سرور اجرا شود تا HTML برای عملکرد و سئو ارائه شود ، اما همچنین می تواند در مشتری اجرا شود تا یک تجربه غنی و تک صفحه را فراهم کند.


Node.Js را وارد کنید: سرانجام یک زمان اجرا سریع ، با مانع کم برای ورود JavaScript برای سرور.


ساخت برنامه های تک صفحه بهتر با استفاده از Node.Js


ما با بررسی چارچوب های برنامه منبع باز که اکنون در دسترس است ، شیرجه خود را در Node.Js آغاز کردیم. اولین چیزی که پیدا کردیم Geddy و Tower بود. اینها به شدت از Rails الهام گرفته شده اند و بنابراین معماری کاملاً سنتی MVC دارند. یک URL به یک کنترل کننده و یک عمل ترسیم می شود و سرور برای هر درخواست یک صفحه از HTML را برمی گرداند. اینها برای راه سخت حل نمی شوند و به نظر من از نقاط قوت Node بهره نمی برند.


بعدی SocketStream بود. SocketStream شسته و رفته است. این سیستم در زمان واقعی خود کاملاً مدولار است و برخی از آداپتورهای شسته و رفته را برای استفاده از WebSockets برای ادامه مدل های Backbone یا Ember در سرور فراهم می کند. با این حال ، برای پشتیبانی از برنامه های کاملاً سمت مشتری ساخته شده است: معروف به راه آسان.


نفر بعدی شهاب سنگ بود. Meteor هیجان انگیزترین چارچوب برنامه Node این مجموعه است و به نظر می رسد راهی انقلابی برای نوشتن برنامه است. مانند SocketStream ، در زمان واقعی است ، اما ساختار بسیار بیشتری در برنامه شما ایجاد می کند و شما باید از طریق Meteor Way خرید کنید. این حتی سیستم پلاگین خود را فراهم می کند و NPM را به نفع یک روش خاص Meteor فرار می کند. این کمی من را می ترساند. چرا چرخ را دوباره اختراع کرد؟ سرانجام ، برای راه سخت حل نمی شود. این یک اشتباه در صحبت من بود - من گفتم که ارائه روی سرور و سرویس گیرنده یک اصل اصلی Meteor بود ، اما مشخص شد که فقط یک پلاگین هکی دارد که از PhantomJS برای راه اندازی یک نمونه WebKit بدون سرور در سرور استفاده می کند ، و در مستندات با عنوان "استفاده نکنید"


بالاخره دربی دربی تنها پروژه Node است که من دیده ام و در واقع برای راه سخت به عنوان یک هدف طراحی حل می شود. این یک رویکرد جدید برای به اشتراک گذاری الگوها و منطق برنامه بین مشتری و سرور دارد ، که به شدت به موتور مسابقه همزمان سازی مدل مبتنی بر MongoDB متکی است. با این حال ، مانند Meteor ، بسیار مدولار نیست و بسیار آلفا است. همانطور که در صحبت اشاره کردم بسیار مشتاق شدم که به دربی چرخش دهم و پس از آن با نیت اسمیت ، همکاری کننده اصلی دربی ، به گفتگو نشستم تا ببینم چگونه ممکن است از آن استفاده کنیم. همانطور که می ترسیدم ، برای زمان نخست آماده نیست ، به خصوص اگر مثل ما ، داده های شما از مجموعه RESTful API ها گرفته می شود که از برنامه Node جدا شده اند.


ستون فقرات در سرور؟


بنابراین ، ما نمونه برداری از چارچوب برنامه Node خود را آغاز کرده ایم. برای دادن جزئیات خیلی زود است ، اما ما با انتقال مدل ها ، مجموعه ها و نماها به سبک Backbone به Node شروع می کنیم و می بینیم چه مشکلی پیش می آید. ما از کپسول Henrik Joretag و نمای Mikito Takada.Json الهام گرفته ایم. اخبار هیجان انگیزتری در این باره! اگر این رویکردی است که شما را علاقه مند می کند ، از طریق نظرات و توییتر به نشانی @ spikebrehm به من اطلاع دهید.


گفتگوی فناوری گذشته و آینده


برای دیدن فیلم های گفتگوهای گذشته و برنامه زمانبندی گفتگوهای آینده ، به صفحه بحث فنی ما مراجعه کنید. اگر اهل یک سان فرانسیسکو هستید ، بیایید چک کنید!

۰
۰
عرفان ندیمی
عرفان ندیمی
شاید از این پست‌ها خوشتان بیاید