متن خبر

نحوه مدیریت عوارض جانبی در جست – راهنمای تمسخر موثر

نحوه مدیریت عوارض جانبی در جست – راهنمای تمسخر موثر

شناسهٔ خبر: 754005 -




تست واحد یک موضوع اصلی برای هر توسعه دهنده است. این یک تمرین اساسی در ساخت برنامه های نرم افزاری است. تست واحد به شما کمک می کند تا اشکالات را زودتر شناسایی کنید و نگهداری کد را آسان تر می کند. با جداسازی و آزمایش تک واحدها یا اجزای برنامه خود، می توانید از قابلیت اطمینان و عملکرد آنها اطمینان حاصل کنید.

هنگام استفاده از تست واحد، باید روی منطق اصلی یک مؤلفه بدون تأثیر بر وابستگی‌های خارجی یا ایجاد عوارض جانبی تمرکز کنید - تغییرات ناخواسته‌ای که خارج از محدوده یک تابع رخ می‌دهند، مانند پرس و جوهای پایگاه داده یا درخواست‌های شبکه.

Jest یک چارچوب تست محبوب است که قابلیت های قدرتمندی را برای کمک به تست موثر ارائه می دهد. Mocking in Jest به شما کمک می کند وابستگی های خارجی را آزمایش و مدیریت کنید و عوارض جانبی را کنترل کنید.

در این راهنما، شما در مورد موارد ضروری تست واحد، تمرکز بر جست و جو می‌آموزید. چه تازه شروع کرده باشید و چه به دنبال ارتقای استراتژی تست خود باشید، این راهنما شما را با دانش نوشتن تست های موثر و کارآمد مجهز می کند.

در اینجا چیزی است که ما پوشش خواهیم داد:

تست واحد چیست؟

وابستگی های خارجی چیست؟

عوارض جانبی چیست؟

تمسخر چیست؟

مورد استفاده: Login Express Controller

خلاصه

تست واحد چیست؟

تست واحد یک تکنیک تست نرم افزاری است که برای آزمایش یک جزء از برنامه شما به صورت مجزا استفاده می شود. این جزء ممکن است یک کلاس، یک متد یا یک ماژول باشد.

چرا باید از تست واحد استفاده کنید

    شما قادر خواهید بود اشکالات را زودتر تشخیص دهید، این به شما کمک می کند تشخیص دهید که آیا یک جزء مطابق انتظار عمل می کند یا خیر.

    به شما این امکان را می دهد که مؤلفه خود را با خیال راحت تغییر دهید. اگر کامپوننت خود را به‌روزرسانی کنید و به اشتباه چیزی را که نباید اضافه یا تغییر دهید، در صورتی که این تغییرات باگ جدیدی ایجاد کند، آزمایش ناموفق خواهد بود.

    این می تواند به عنوان سندی عمل کند که نشان می دهد واحدهای جداگانه برنامه شما چگونه کار می کنند.

    شما را به نوشتن کدهای پاک تر تشویق می کند. هرچه کامپوننت شما تمیزتر باشد، تست شما آسانتر و ساده تر خواهد بود.

    این به شما کمک می کند تا به راحتی بخش های مختلف برنامه خود را ادغام کنید، زیرا مطمئن خواهید بود که هر جزء به درستی کار می کند.

    در دراز مدت، می توانید برنامه خود را سریعتر حفظ کنید.

اجازه دهید به طور عمیق به چند کاربرد عملی بپردازیم:

فرض کنید یک تابع ضرب دارید که باید دو آرگومان بگیرد و نتیجه را برگرداند.

این هم کد:

 function multiply ( a,b ) { return a*b } export default multiply

توجه : برای استفاده از Jest با ماژول‌های Node.js ECMAScript، این مقاله را برای پیکربندی تحلیل کنید.

پس چگونه می توانید این تابع را با استفاده از Jest آزمایش کنید؟

    پوشه __tests__ را در پوشه root ایجاد کنید.

    فایل multiply.test.js را در داخل __tests__ ایجاد کنید.

    توجه داشته باشید که هر فایلی که به .test.js ختم شود توسط Jest اجرا خواهد شد.

    نوشتن تست های خود را با فراخوانی متد it("",()=>{}) Jest شروع کنید.

بیایید بفهمیم که ` it("",()=>{}) ` چه کاری انجام می دهد:

متد it یک تابع Jest است که برای آزمایش برخی رفتارها در عملکرد شما استفاده می شود.
اولین آرگومان باید نام آزمون باشد که می تواند یک متن ادعایی برای آنچه از این آزمون انتظار دارید باشد.

به عنوان مثال، اگر باید آزمایش کنید که آیا تابع multiply با استفاده از آرگومان‌ها نتیجه درست را برمی‌گرداند یا خیر، می‌توانید it("should return the multiplication of inputs of type number",()=>{}) .

آرگومان دوم تابعی برای منطق تست شما است. هنگامی که تست خود را اجرا می کنید، فراخوانی می شود .

برای نوشتن موثر آزمون‌های خود، باید الگوی AAA (Arrange-Act-Assert) را اعمال کنید.

    ترتیب : داده ها را تنظیم کنید یا وابستگی هایی را که در این تست استفاده می کنید پیکربندی کنید.

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

    ادعا کنید: انتظارات خود را بنویسید - انتظار دارید عملکردی که آزمایش می کنید چگونه رفتار کند. برای ادعا، شما همیشه از روش expect جست استفاده خواهید کرد.

هر عبارت it("",()=>{}) را به عنوان سناریویی متفاوت از عملکرد خود در نظر بگیرید.

در اینجا یک مثال است:

 import multiply from './../multiply.js' it( "should return the multiplication of inputs of type number" , () => { // Arrange const testArg1 = 5 ; const testArg2 = 2 ; // Act const result = multiply(testArg1, testArg2); // Assert expect(result).toBe( 10 ); }); it( "should returns NaN if no arguments are passed" , () => { // Arrange // Act const result = multiply(); // Assert expect(result).toBeNaN(); }); it( "should returns NaN if only one argument is passed" , () => { // Arrange const arg = 5 ; // Act const result = multiply(arg); // Assert expect(result).toBeNaN(); }); it( "should returns Zero if one of the arguments is empty string" , () => { // Arrange const testArg1 = "" ; const testArg2 = 5 ; // Act const result = multiply(testArg1, testArg2); // Assert expect(result).toBe( 0 ); });

این تست ها از جمله تست هایی هستند که می توانید به فایل خود اضافه کنید. بسته به سناریوهای مختلف عملکردی که آزمایش می کنید، می توانید تست های بیشتری اضافه کنید یا برخی را حذف کنید.

وابستگی های خارجی چیست؟

وابستگی های خارجی ماژول ها یا توابعی هستند که کد شما بر آنها تکیه دارد و خارج از پایگاه کد شما منشأ می گیرد. اینها می توانند شامل کتابخانه ها، API ها، پایگاه های داده، توابع یا هر سرویسی باشد که برنامه شما با آن تعامل دارد.

آزمایش با وابستگی های خارجی می تواند چالش برانگیز باشد زیرا:

آنها می توانند تست ها را به دلیل تاخیر در شبکه یا پردازش کند کنند.

ممکن است در طول آزمایش در دسترس نباشند، که به نوبه خود باعث خرابی می شود.

همانطور که در تابع زیر نشان داده شده است، اگر تابع شما تابع دیگری را فراخوانی کند چه؟ اکثر توابعی که روزانه می نویسید در واقع توابع دیگر را فراخوانی می کنند.

یعنی:

 function processNumbers ( numbers, callback ) { // numbers: array // callback: function return numbers.map(callback); } export default processNumbers;

هنگام اعمال تست واحد، واحدها باید به صورت مجزا آزمایش شوند. تابع processNumbers به ​​یک callback دیگر بستگی دارد.

پس در این مورد چه باید کرد؟ تمسخر راه حل است و بعداً در بخش دیگری در مورد آن صحبت خواهیم کرد.

عوارض جانبی چیست؟

عوارض جانبی زمانی اتفاق می‌افتد که یک تابع حالتی خارج از محدوده خود را تغییر می‌دهد یا جدا از برگرداندن یک مقدار، تعاملات قابل مشاهده با دنیای خارج دارد.

به عنوان مثال می توان به تغییر یک متغیر جهانی، تغییر سیستم فایل یا ارسال درخواست HTTP اشاره کرد.

عوارض جانبی می‌تواند آزمایش‌ها را غیرقابل پیش‌بینی و مدیریت آن‌ها دشوار کند، زیرا:

ممکن است با سیستم های دیگر تعامل داشته باشد و باعث تغییر حالت های خارجی شود.

اگر به درستی ایزوله نشود، می تواند منجر به تست های پوسته پوسته شود.

در اینجا یک مثال است که یک کاربر را از پایگاه داده با استفاده از id خود باز می گرداند:

 async function getUserFromDatabase ( userId ) { // Simulates fetching from a database return { id : userId, name : 'John' }; } export {getUserFromDatabase}

در اینجا تابع دیگری است که از getUserFromDatabase در کد بالا استفاده می کند:

 async function getProfile ( userId ) { return await getUserFromDatabase(userId); } export default getProfile

هنگام آزمایش این تابع، در واقع نباید یک درخواست واقعی ارسال کنید، تنها چیزی که نیاز دارید این است که رفتار تابع getProfile را بدون ضربه زدن به هیچ سیستم خارجی آزمایش کنید.

برای حل این وضعیت می توانید از تمسخر نیز استفاده کنید.

تمسخر چیست؟

تمسخر در مورد شبیه سازی است—شما باید تابعی را که در حال آزمایش آن هستید جدا کنید. اگر عملکرد به هر وابستگی خارجی متکی است یا ممکن است عوارض جانبی ایجاد کند، باید رفتار آن جنبه ها را شبیه سازی کنید.

تمسخر شامل ایجاد یک نسخه جعلی از یک تابع، شی یا ماژول برای کنترل رفتار آن در طول آزمایش است. این به شما امکان می دهد سناریوهای مختلف را شبیه سازی کنید و تعاملات را بدون تکیه بر پیاده سازی های واقعی تأیید کنید.

ما بر دو رویکرد برای تمسخر تمرکز خواهیم کرد:

    Function Mocks (همچنین جاسوس نامیده می شود):
    شما می توانید از jest.fn() برای ایجاد یک تابع ساختگی استفاده کنید که می تواند برای ردیابی یک تابع یا جایگزینی پیاده سازی واقعی استفاده شود. یا از jest.spyOn(object, methodName) برای ردیابی تماس های object[methodName] استفاده کنید.

    ماژول Mocks : می توانید از jest.mock(“path-of-your-module”) برای مسخره کردن کل ماژول ها یا واردات خاص استفاده کنید. با استفاده از آن، تمام توابع داخل این ماژول به توابع ساختگی تبدیل می شوند. علاوه بر این، در طول تست، ماژول هایی که شما در حال آزمایش آن هستید، نسخه تقلبی این ماژول را دریافت خواهند کرد.

هر تابع ساختگی روش هایی دارد که می توانید از آنها برای شبیه سازی رفتار تابع استفاده کنید. برخی از پرکاربردترین روش ها عبارتند از:

mockFn.mockImplementation(fn) : برای جایگزینی اجرای واقعی یک تابع استفاده می شود. fn اجرای جایگزین است.

mockFn.mockReturnValue(value) : اگر تنها چیزی که به آن اهمیت می دهید مقدار بازگشتی یک تابع است، می توانید از این استفاده کنید.

mockFn.mockResolvedValue(value) : اگر تابع mock یک وعده را برمی گرداند، می توانید از این استفاده کنید.

مثال استفاده 1

بیایید processNumbers با استفاده از mock های تابع آزمایش کنیم. چالش اینجاست که processNumbers تابع callback را به عنوان آرگومان می گیرد. اگر نیاز به آزمایش این تابع فراخوانی در داخل processNumbers داشته باشید، چه؟

این هم کد:

 import processNumbers from 'file-path' ; test( 'processNumbers applies callback and return the right result' , () => { // Arrange const arr = [ 2 , 3 ] const mockedCallback = jest.fn().mockImplementation( x => x + 2 ); // Act const result = processNumbers(arr, mockedCallback); // Assert expect(result).toEqual([ 4 , 5 ]); expect(mockedCallback).toHaveBeenCalledTimes(arr.length); });

ما با ترتیب دادن استدلال ها شروع کردیم:

متغیر arr آرایه ای از اعداد است. در آزمون یک آرایه با اعداد تصادفی به آن اختصاص دادیم.

متغیر callback یک تابع callback است. این تابع باید در آزمون مورد تمسخر قرار گیرد.

ممکن است از خود بپرسید که چرا باید callback مسخره کنید، چرا آن را به عنوان یک عملکرد عادی اختصاص نمی دهید؟

پاسخ این است که بدون تمسخر آرگومان callback ، نمی‌توانید آن را در داخل processNumbers در حین آزمایش آن ردیابی کنید. از آنجایی که تمسخر یک نسخه جعلی از تابع ایجاد می‌کند، جاسوسی ایجاد می‌کند که دارای ردیاب است که از طریق آن می‌توانید هر اقدامی را که در این تابع مسخره‌شده انجام می‌شود، چه فراخوانی شود و چه آرگومان‌هایی به آن ارسال شود، ابراز کنید.

jest.fn() یک تابع ساختگی ایجاد می کند. شما می توانید یک تابع را به جای تابع واقعی به fn ارسال کنید.

در مرحله بعد، با فراخوانی تابعی که در حال آزمایش هستیم، "عمل" می کنیم: processNumbers .

در نهایت، ما ادعاهایی را نوشتیم، که انتظاراتی در مورد نحوه رفتار processNumbers و اعمال callback توسط processNumbers و برگرداندن نتیجه هستند.

مثال استفاده 2

عوارض جانبی جنبه دیگری است که باید در آزمایش کنترل کنید. در تابع getProfile یک سیستم خارجی فراخوانی می شود که یک پایگاه داده را برای بازیابی اطلاعات فراخوانی می کند و این یک عارضه جانبی است.

در سناریویی دیگر، یک تابع ممکن است یک پایگاه داده را برای ایجاد یک کاربر متصل کند و از طریق آزمایش، نیازی به گفت ن یا تغییر داده های واقعی در پایگاه داده نخواهید داشت.

برای شبیه سازی رفتار getUserFromDatabase بدون ضربه زدن به پایگاه داده، باید ماژول آن را مسخره کنید، و به طور پیش فرض، getUserFromDatabase یک تابع ساختگی خالی خواهد بود که می توان آن را در طول آزمایش ردیابی کرد.

این هم کد:

 import getProfile from 'file-path' ; import { getUserFromDatabase } from 'file-path' ; // Mock the module of getUserFromDatabase method jest.mock( './../DB/databaseMethods.js' ); describe( 'getProfile' , () => { it( 'should call getUserFromDatabase with the correct userId and return the result' , async () => { // Arrange const userId = '123' ; const dummyUser = { id : userId, name : 'John' }; getUserFromDatabase.mockResolvedValue(dummyUser); // Act const result = await getProfile(userId); // Assert expect(result).toEqual(dummyUser); expect(getUserFromDatabase).toHaveBeenCalledWith(userId); expect(getUserFromDatabase).toHaveBeenCalledTimes( 1 ); }); });

ما با مرتب کردن استدلال ها شروع کردیم:

userId فقط یک عدد است.

dummyUser یک شی است که داده های جعلی کاربر را شبیه سازی می کند.

ما dummyUser از getUserFromDatabas با استفاده از mockResolvedValue برگرداندیم.

مشابه مثال آخر، ما با فراخوانی تابع مورد آزمایش "عمل" می کنیم: getProfile .

در نهایت، ما این اظهارات را نوشتیم، انتظارات شما در مورد اینکه getProfile چگونه باید رفتار کند و اینکه getUserFromDatabase به درستی فراخوانی شده و نتیجه مطابق انتظار برمی گردد.

مورد استفاده: Login Express Controller

در اینجا یک کنترلر ورود وجود دارد که ایمیل و رمز عبور یک کاربر را از طریق شی req دریافت می کند و سپس کاربر را در پایگاه داده جستجو می کند. برخی تحلیل ها را انجام می دهد، سپس اگر همه چیز درست است، یک res برمی گرداند، یا با یک شی خطا، next را فراخوانی می کند.

 import User from "file-path" ; export const login = async (req, res, next) => { const { email, password } = req.body; const user = await User.findOne({ email }); if (!user) return next( new Error ( "Invalid Email!" )); const checkPassword = user.checkPassword(password); if (!checkPassword) return next( new Error ( "Invalid Password!" )); const token = user.generateToken(); return res.status( 200 ).json({ success : true , results : { token } }); };

در مورد مراحلی که می توانید برای آزمایش عملکرد ورود استفاده کنید فکر کنید. می‌توانید چند سؤال بپرسید که به شما در ارائه ایده‌ها کمک می‌کند:

سناریوهای گردش کار تابع login چیست؟

    کاربر پیدا نشد

    رمز عبور نادرست است

    همه چیز اوکی است و یک پاسخ با یک نشانه برگردانده می شود.

پس می توانید برای انجام کارهای زیر login شوید:

اگر کاربر پیدا نشد، باید login next بگیرد.

اگر رمز عبور مطابقت نداشته باشد، login باید next تماس بگیرد.

login باید res.json را با توکن و res.status را با 200 فراخوانی کند اگر همه چیز درست است.

آرگومان هایی که روش login باید دریافت کند چیست؟

    شیء req با خاصیت body .

    شی res با ویژگی status و json .

    تابع next

res.json() یا res.status() یا next() همگی توابعی هستند که login برای انجام کار خود به آنها نیاز دارد. در طول آزمایش، شما به این آرگومان ها دسترسی ندارید، پس باید آنها را مسخره کنید.

req می توان به صورت {body: { email: " test@foo.com ", password: "bar" }} تعریف کرد

res می توان به صورت {json: jest.fn().mockReturnThis(), status: jest.fn().mockReturnThis()} تعریف کرد.

next می توان به عنوان jest.fn() تعریف کرد

آیا تعاملی با سیستم های خارجی یا وابستگی هایی وجود دارد؟

    User.findOne()

    user.checkPassword()

    user.generateToken()

پس تمسخر راه حل است:

برای User.findOne() باید کل ماژول User را مسخره کنید و findOne() جعلی را برای بازگرداندن یک user جعلی تنظیم کنید. چالش اینجاست که findOne یک متد شی است. چگونه می توانید آن را پیگیری کنید؟ jest.spyOn(object, methodName) روح است.
روش spyOn برای ردیابی تماس های object[methodName] استفاده می شود که در مورد ما User.findOne است.

user.checkPassword() و user.generateToken() باید توابع ساختگی باشند.

برای اعمال تمام این مفاهیم و قرار دادن بلوک ها با یکدیگر، آزمون نهایی باید به صورت زیر باشد:

 import User from "file-path" ; import { login } from "file-path" ; jest.mock( "../DB/models/user.model.js" ); let mockReq, mockRes, mockNext, dummyUser; describe( "login controller" , () => { beforeEach( () => { mockReq = { body : { email : "test@foo.com" , password : "bar" } }; mockRes = { json : jest.fn().mockReturnThis(), status : jest.fn().mockReturnThis(), }; mockNext = jest.fn(); dummyUser = { checkPassword : jest.fn( () => true ), generateToken : jest.fn( () => "token" ), }; }); it( "should call next if user not found" , async () => { // Arrange jest.spyOn(User, "findOne" ).mockResolvedValueOnce( null ); // Act await login(mockReq, mockRes, mockNext); // Assert expect(mockNext).toHaveBeenCalledWith( new Error ( "Invalid Email!" )); expect(mockRes.json).not.toHaveBeenCalled(); }); it( "should call next if password doesn't match" , async () => { // Arrange dummyUser.checkPassword.mockReturnValueOnce( false ); jest.spyOn(User, "findOne" ).mockResolvedValue(dummyUser); // Act await login(mockReq, mockRes, mockNext); // Assert expect(mockNext).toHaveBeenCalledWith( new Error ( "Invalid Password!" )); expect(dummyUser.generateToken).not.toHaveBeenCalled(); expect(mockRes.json).not.toHaveBeenCalled(); }); it( "should call res.json with the token and call res.status with 200 if everything is ok" , async () => { // Arrange jest.spyOn(User, "findOne" ).mockResolvedValue(dummyUser); // Act await login(mockReq, mockRes, mockNext); // Assert expect(mockNext).not.toHaveBeenCalled(); expect(User.findOne).toHaveBeenCalledWith({ email : mockReq.body.email }); expect(dummyUser.checkPassword).toHaveBeenCalledWith(mockReq.body.password); expect(dummyUser.generateToken).toHaveBeenCalled(); expect(mockRes.status).toHaveBeenCalledWith( 200 ); expect(mockRes.json).toHaveBeenCalledWith({ success : true , results : { token : "token" }, }); }); });

نکته پایانی : beforeEach یک قلاب Jest باشد، می توانید از آن برای پیاده سازی کد قبل از هر تست استفاده کنید. در داخل beforeEach تابع، می‌توانید هر متغیر مشترکی را که آزمون‌های شما ممکن است به آن نیاز داشته باشند، بنویسید، به‌جای اینکه آن‌ها را به‌طور مستقل برای هر آزمون بنویسید.

خلاصه

در این آموزش اصول اولیه تست واحد با جست را با تمرکز بر نحوه استفاده از ماک ها آموختید. تست واحد کمک می کند تا اطمینان حاصل شود که بخش های جداگانه کد شما به طور مجزا به درستی کار می کنند.

مدیریت وابستگی های خارجی، مدیریت عوارض جانبی و استفاده از تمسخر، مهارت های ضروری برای آزمایش قوی هستند. Jest ابزارهای قدرتمندی برای مقابله با این چالش‌ها ارائه می‌کند و آزمایش‌های شما را قابل اعتمادتر، سریع‌تر و آسان‌تر نگه می‌دارد.

درک این مفاهیم به شما کمک می کند تا تست های بهتری بنویسید و برنامه های کاربردی انعطاف پذیرتری تولید کنید.

در این آموزش نحوه استفاده از ویژگی های تمسخر جست برای شبیه سازی وابستگی های خارجی و مدیریت عوارض جانبی توضیح داده شده است. این شامل یک مثال عملی از آزمایش یک کنترل کننده ورود Express.js است که نشان می دهد چگونه عملکردها و سناریوهای آزمایش را کنترل کنید.

این رویکرد به شما کمک می‌کند تا با جداسازی و مدیریت مؤثر وابستگی‌ها، تست‌های قابل اعتمادی ایجاد کنید و کیفیت کد را حفظ کنید.

خبرکاو

ارسال نظر

دیدگاه‌ها بسته شده‌اند.


تبليغات ايهنا تبليغات ايهنا

تمامی حقوق مادی و معنوی این سایت متعلق به خبرکاو است و استفاده از مطالب با ذکر منبع بلامانع است