ابزارهای تجارت

تست‌های خودکار اساساً فقط کدی هستند که در صورت اشتباه بودن، باعث ایجاد خطا یا خطا می‌شوند. اکثر کتابخانه‌ها یا چارچوب‌های آزمایشی انواع اولیه‌ای را ارائه می‌کنند که نوشتن تست‌ها را آسان‌تر می‌کند.

همانطور که در بخش قبل ذکر شد، این موارد اولیه تقریباً همیشه شامل راهی برای تعریف تست‌های مستقل (که به آن‌ها به عنوان موارد تست اشاره می‌شود) و ارائه ادعاها می‌شوند. اظهارات روشی برای ترکیب بررسی یک نتیجه و پرتاب خطا در صورت اشتباه است و می‌توان آن را اصلی اولیه همه آزمایش‌های اولیه در نظر گرفت.

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

مثلا:

import { fibonacci, catalan } from '../src/math.js';
import { assert, test, suite } from 'a-made-up-testing-library';

suite('math tests', () => {
  test('fibonacci function', () => {
    // check expected fibonacci numbers against our known actual values
    // with an explanation if the values don't match
    assert.equal(fibonacci(0), 0, 'Invalid 0th fibonacci result');
    assert.equal(fibonacci(13), 233, 'Invalid 13th fibonacci result');
  });
  test('relationship between sequences', () => {
    // catalan numbers are greater than fibonacci numbers (but not equal)
    assert.isAbove(catalan(4), fibonacci(4));
  });
  test('bugfix: check bug #4141', () => {
    assert.isFinite(fibonacci(0)); // fibonacci(0) was returning NaN
  })
});

این مثال گروهی از تست‌ها را ایجاد می‌کند (گاهی اوقات مجموعه نامیده می‌شود) به نام "تست‌های ریاضی"، و سه مورد تست مستقل را تعریف می‌کند که هر کدام برخی از ادعاها را اجرا می‌کنند. این موارد تست معمولاً می‌توانند به صورت جداگانه مورد بررسی قرار گیرند یا اجرا شوند، به عنوان مثال، توسط یک پرچم فیلتر در اجرای آزمایشی شما.

مددکاران به عنوان افراد بدوی

اکثر چارچوب‌های آزمایشی، از جمله Vitest، شامل مجموعه‌ای از کمک‌کننده‌های اظهارنظر بر روی یک شیء assert هستند که به شما امکان می‌دهند به سرعت مقادیر بازگشتی یا سایر حالت‌ها را برخلاف انتظارات بررسی کنید. این انتظار اغلب ارزش های "خوب شناخته شده" است. در مثال قبلی، می‌دانیم که سیزدهمین عدد فیبوناچی باید ۲۳۳ باشد، بنابراین می‌توانیم مستقیماً با استفاده از assert.equal تأیید کنیم.

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

  • یک بررسی «درست» ، که اغلب به عنوان چک «ok» توصیف می‌شود، درستی یک شرط را بررسی می‌کند و با if نوشتن یک علامت مطابقت دارد که بررسی می‌کند آیا چیزی موفق یا درست است. این معمولاً به‌عنوان assert(...) یا assert.ok(...) ارائه می‌شود و یک مقدار واحد به اضافه یک نظر اختیاری می‌گیرد.

  • یک بررسی برابری، مانند مثال آزمون ریاضی، که در آن انتظار دارید مقدار بازگشتی یا وضعیت یک شی برابر با مقدار خوب شناخته شده باشد. اینها برای برابری اولیه (مانند اعداد و رشته ها) یا برابری ارجاعی هستند (آیا اینها همان شی هستند). در زیر کاپوت، اینها فقط یک بررسی "حقیقت" با مقایسه == یا === هستند.

    • جاوا اسکریپت بین برابری آزاد ( == ) و سخت ( === ) تمایز قائل می شود. اکثر کتابخانه های آزمایشی به ترتیب متدهای assert.equal و assert.strictEqual را در اختیار شما قرار می دهند.
  • بررسی‌های تساوی عمیق، که بررسی‌های برابری را به بررسی محتوای اشیا، آرایه‌ها و دیگر انواع داده‌های پیچیده‌تر و همچنین منطق داخلی برای عبور از اشیاء برای مقایسه آنها گسترش می‌دهند. اینها مهم هستند زیرا جاوا اسکریپت هیچ روش داخلی برای مقایسه محتویات دو شی یا آرایه ندارد. به عنوان مثال، [1,2,3] == [1,2,3] همیشه نادرست است. فریم ورک های تست اغلب شامل کمک کننده های deepEqual یا deepStrictEqual هستند.

کمک‌کننده‌های ادعایی که دو مقدار را مقایسه می‌کنند (به‌جای بررسی «حقیقت») معمولاً دو یا سه آرگومان می‌گیرند:

  • مقدار واقعی، همانطور که از کد تحت آزمایش یا توصیف وضعیت برای اعتبار سنجی ایجاد می شود.
  • مقدار مورد انتظار، معمولاً با کد سخت (به عنوان مثال، یک عدد تحت اللفظی یا یک رشته).
  • یک نظر اختیاری که توضیح می دهد چه چیزی مورد انتظار است یا چه چیزی ممکن است شکست خورده باشد، که در صورت عدم موفقیت این خط شامل خواهد شد.

همچنین ترکیب اظهارات برای ایجاد انواع چک، عمل نسبتاً رایجی است، زیرا به ندرت پیش می‌آید که فردی بتواند به تنهایی وضعیت سیستم شما را به درستی تأیید کند. مثلا:

  test('JWT parse', () => {
    const json = decodeJwt('eyJieSI6InNhbXRob3Ii…');

    assert.ok(json.payload.admin, 'user should be admin');
    assert.deepEqual(json.payload.groups, ['role:Admin', 'role:Submitter']);
    assert.equal(json.header.alg, 'RS265')
    assert.isAbove(json.payload.exp, +new Date(), 'expiry must be in future')
  });

Vitest از کتابخانه ادعای Chai به صورت داخلی برای ارائه کمک‌کننده‌های ادعای خود استفاده می‌کند و می‌تواند مفید باشد که از طریق مرجع آن نگاه کنید تا ببینید چه ادعاها و کمک‌هایی ممکن است با کد شما مطابقت داشته باشند.

اظهارات روان و BDD

برخی از توسعه دهندگان سبک ادعایی را ترجیح می دهند که می توان آن را توسعه رفتار محور (BDD) یا ادعاهای سبک روان نامید. به اینها کمک‌کننده‌های "انتظار" نیز می‌گویند، زیرا نقطه ورود به بررسی انتظارات، متدی به نام expect() است.

انتظار داشته باشید که کمک‌کننده‌ها مانند ادعاهایی که به‌عنوان فراخوانی روش‌های ساده مانند assert.ok یا assert.strictDeepEquals نوشته شده‌اند، رفتار کنند، اما برخی از توسعه‌دهندگان خواندن آنها را آسان‌تر می‌دانند. یک ادعای BDD ممکن است به صورت زیر باشد:

// A failure here would generate "Expect result to be an array that does include 42"
const result = await possibleMeaningsOfLife();
expect(result).to.be.an('array').that.does.include(42);

// or a simpler form
expect(result).toBe('array').toContainEqual(42);

// the same in assert might be
assert.typeOf(result, 'array', 'Expected the result to be an array');
assert.include(result, 42, 'Expected the result to include 42');

این سبک از ادعاها به دلیل تکنیکی به نام متد زنجیره‌سازی کار می‌کنند، که در آن شیء برگشت‌داده‌شده توسط expect می‌تواند به‌طور پیوسته با فراخوانی‌های متد بیشتر به هم زنجیر شود. برخی از بخش‌های تماس، از جمله to.be و that.does در مثال قبلی، هیچ عملکردی ندارند و فقط برای آسان‌تر خواندن تماس و ایجاد نظر خودکار در صورت شکست تست، گنجانده شده‌اند. (به‌خصوص، expect معمولاً از یک نظر اختیاری پشتیبانی نمی‌کند، زیرا زنجیره‌سازی باید شکست را به وضوح توصیف کند.)

بسیاری از چارچوب‌های آزمایشی از Fluent/BDD و ادعاهای معمولی پشتیبانی می‌کنند. برای مثال ، Vitest، هر دو رویکرد چای را صادر می کند و رویکرد کمی مختصرتر خود را به BDD دارد. از طرف دیگر، Jest به طور پیش فرض فقط شامل یک متد انتظار می شود.

تست های گروهی در بین فایل ها

هنگام نوشتن تست‌ها، ما قبلاً تمایل به ارائه گروه‌بندی ضمنی داریم - به جای اینکه همه آزمایش‌ها در یک فایل باشند، نوشتن تست‌ها در چندین فایل معمول است. در واقع، اجراکنندگان آزمایش معمولاً فقط می‌دانند که یک فایل برای آزمایش است، زیرا یک فیلتر از پیش تعریف شده یا عبارت معمولی وجود دارد. ts" (."test" و ".spec" به اضافه تعدادی پسوند معتبر).

آزمون های مؤلفه معمولاً در یک فایل مشابه برای مؤلفه تحت آزمایش قرار می گیرند، مانند ساختار دایرکتوری زیر:

فهرستی از فایل‌ها در یک فهرست، از جمله UserList.tsx و UserList.test.tsx.
یک فایل کامپوننت و فایل تست مربوطه.

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

تست های گروهی درون فایل ها

همانطور که در مثال‌های قبلی استفاده شد، قرار دادن تست‌ها در داخل یک call to suite() که تست‌هایی را که با test() تنظیم کرده‌اید گروه‌بندی می‌کند، معمول است. مجموعه‌ها معمولاً خود تست نیستند، اما با گروه‌بندی تست‌ها یا اهداف مرتبط با فراخوانی روش قبول شده، به ارائه ساختار کمک می‌کنند. برای test() ، متد تصویب شده اقدامات خود آزمون را توصیف می کند.

همانطور که در مورد ادعاها، یک معادل نسبتا استاندارد در Fluent/BDD برای گروه بندی تست ها وجود دارد. برخی از نمونه های معمولی در کد زیر مقایسه شده اند:

// traditional/TDD
suite('math tests', () => {
  test('handle zero values', () => {
    assert.equal(fibonacci(0), 0);
  });
});

// Fluent/BDD
describe('math tests', () => {
  it('should handle zero values', () => {
    expect(fibonacci(0)).toBe(0);
  });
})

در اکثر چارچوب‌ها، suite و describe رفتار مشابهی دارند، مانند test و it ، برخلاف تفاوت‌های بیشتر بین استفاده از expect و assert برای نوشتن ادعاها.

سایر ابزارها رویکردهای متفاوتی برای تنظیم مجموعه ها و تست ها دارند. به عنوان مثال، اجراکننده تست داخلی Node.js از فراخوانی های تودرتو به test() پشتیبانی می کند تا به طور ضمنی یک سلسله مراتب آزمایشی ایجاد کند. با این حال، Vitest فقط این نوع تودرتو با استفاده از suite() اجازه می دهد و یک test() تعریف شده در داخل test() دیگر را اجرا نمی کند.

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

روش های چرخه حیات

یک دلیل برای گروه بندی تست های خود، حتی به طور ضمنی در سطح بالا در یک فایل، ارائه روش های راه اندازی و پاک کردن است که برای هر تست یا یک بار برای گروهی از تست ها اجرا می شود. اکثر چارچوب ها چهار روش را ارائه می دهند:

برای هر «test()» یا «it()». یک بار برای سوئیت
قبل از اجرای آزمایشی "قبل از هر()". «پیش از همه ()».
پس از اجرای آزمایشی «afterEach()». «afterAll()».

به عنوان مثال، ممکن است بخواهید قبل از هر آزمایش یک پایگاه داده کاربر مجازی را از قبل پر کنید و سپس آن را پاک کنید:

suite('user test', () => {
  beforeEach(() => {
    insertFakeUser('bob@example.com', 'hunter2');
  });
  afterEach(() => {
    clearAllUsers();
  });

  test('bob can login', async () => { … });
  test('alice can message bob', async () => { … });
});

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

توصیه های عمومی

در اینجا چند نکته وجود دارد که باید هنگام فکر کردن به این چیزهای اولیه به خاطر بسپارید.

بدوی ها یک راهنما هستند

به یاد داشته باشید که ابزارها و ابزارهای اولیه در اینجا و در چند صفحه بعدی دقیقاً با Vitest، Jest، یا Mocha، یا Web Test Runner یا هر فریمورک خاص دیگری مطابقت ندارند. در حالی که ما از Vitest به عنوان یک راهنمای کلی استفاده کرده‌ایم، حتماً آنها را با چارچوب انتخابی خود ترسیم کنید.

در صورت نیاز، اظهارات را با هم ترکیب و مطابقت دهید

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

اما اگر آن رانر کمک‌های assert() ، expect() و assertion را نیز ارائه می‌دهد، به یاد داشته باشید که این بخش بیشتر در مورد راحتی است و در صورت نیاز می‌توانید از آن صرفنظر کنید. می‌توانید هر کدی را که ممکن است خطا ایجاد کند، اجرا کنید، از جمله دیگر کتابخانه‌های ادعا ، یا یک دستور if قدیمی.

راه اندازی IDE می تواند نجات دهنده باشد

اطمینان از اینکه IDE شما، مانند VSCode، به تکمیل خودکار و مستندات مربوط به ابزار تست انتخابی شما دسترسی دارد، می‌تواند شما را بهره‌ورتر کند. به عنوان مثال، بیش از 100 روش در کتابخانه assert Chai وجود دارد، و داشتن مستندات مناسب به صورت درون خطی می تواند راحت باشد.

این می تواند به ویژه برای برخی از چارچوب های آزمایشی که فضای نام جهانی را با روش های آزمایشی خود پر می کنند، مهم باشد. این یک تفاوت ظریف است، اما اغلب می‌توان از کتابخانه‌های آزمایشی بدون وارد کردن آن‌ها استفاده کرد، اگر به‌طور خودکار به فضای نام جهانی اضافه شوند:

// some.test.js
test('using test as a global', () => { … });

توصیه می‌کنیم کمک‌کننده‌ها را حتی اگر به‌طور خودکار پشتیبانی می‌شوند، وارد کنید، زیرا به IDE شما راهی واضح برای جستجوی این روش‌ها می‌دهد. (ممکن است در هنگام ساخت React با این مشکل مواجه شده باشید، زیرا برخی از پایگاه های کد دارای React جهانی جادویی هستند، اما برخی چنین نیستند، و باید آن را در همه فایل ها با استفاده از React وارد کنید.)

// some.test.js
import { test } from 'vitest';
test('using test as an import', () => { … });