برنامه نویسی

داده های تمسخر آمیز برای تست های یکپارچه سازی در React

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

آیا باید کدم را تست کنم؟

اجازه دهید ابتدا این نکته را روشن کنیم زیرا می دانم که برخی از توسعه دهندگان در مورد آن شک دارند.
بدون آزمایش - بدون مشکل

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

تست اول

اجازه دهید فناوری‌هایی را که در مثال‌های زیر استفاده می‌شوند، تعریف کنم. برای توسعه از React (18.2)، Typescript (4.9.3) و Jest (^27) با React Testing Library (13.4.0) برای آزمایش استفاده می شود. اکنون می توانیم کدنویسی را شروع کنیم.
تصور کنید مولفه ای مانند این دارید:

const Users = () => (
  <Page testId={Id.UsersPage} className={classes.usersPage}>
    <PageTitle title="Users" testId={Id.PageTitle} />
    <UsersList testId={Id.UsersList} />
  </Page>
);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما هیچ منطقی در اینجا نداریم، بنابراین بررسی می کنیم که آیا اجزاء پس از رندر قابل مشاهده هستند یا خیر:

it('Users is rendered correctly', () => {
  render(<Users />);

  expect(screen.getByTestId(E2eId.UsersPage)).toBeVisible();
  expect(screen.getByTestId(E2eId.PageTitle)).toBeVisible();       
  expect(screen.getByTestId(E2eId.UsersList)).toBeVisible();  
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

خوب، پس اکنون می دانیم که تمام قسمت های آن Users جزء ارائه شده است. از نظر تئوری، ما می‌توانیم نشانه‌گذاری را به جای آن آزمایش کنیم، اما نتایج زیادی را نشان نمی‌دهد، زیرا چنین آزمایشی شکننده خواهد بود و منطق تجاری را که ممکن است در مؤلفه‌ها داشته باشیم بررسی نمی‌کند.
در زندگی واقعی، آزمون به این آسانی نخواهد بود زیرا در داخل UsersList ما احتمالاً درخواستی برای لیست کاربران به روش زیر داریم:

import { getUsers } from 'api';

const UsersList = observer(() => {
  const [users, setUsers] = useState([]);
  ...
  useEffect(() => {
     getUsers().then((response: User[]) => {
       setUsers(response)
     });   
  }, []);
  ...
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای آزمایش به مؤلفه بالا نگاه کنید Users ما باید تماس API را در اینجا و jest.mock به ما این امکان را می دهد که به راحتی این کار را انجام دهیم.

jest.mock('api', () => ({
  getUsers: () => Promise.resolve([])
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

تست قلاب های سفارشی

از نظر تمسخر رایج ترین مورد برای من، این یک قلاب سفارشی است که برای دریافت داده از فروشگاه استفاده کردم. من MobX را برای مدیریت و استفاده از ایالت انتخاب کردم useStore برای درخواست و دستکاری داده ها قلاب کنید. در اینجا نسخه به روز شده است UsersList جزء.

const UsersList = observer(() => {
  const {
    usersStore: { getUsers }
  } = useStore();

  useEffect(() => {
    getUsers();
  }, []);
  ...
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بیایید سعی کنیم این قلاب را مسخره کنیم و داده ها را برگردانیم:

import { MOCKED_USERS } from 'mocks';

jest.mock('stores/store.context', () => ({
  useStore: () => ({
    usersStore: {
      getUsers: jest.fn(() => Promise.resolve(MOCKED_USERS))
    }
  })
}))
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر این را امتحان کنید، می بینید که کار نمی کند.

اول از همه، شما نمی توانید از متغیرهایی که خارج از آن تعریف شده اند استفاده کنید jest.mock تابع. برای حل آن می توانید هک کنید jest.mock با استفاده از توابع تو در تو، و در برخی موارد، کار می کند.
با این حال، من به چنین راه حلی متکی نیستم و زمانی که شما نیاز به بازگرداندن مجموعه داده های مختلف برای آزمایش های مختلف دارید، کمکی نمی کند (به جز اگر می خواهید از switch…case استفاده کنید). بنابراین، پس از چند آزمایش، به ایده دیگری رسیدم و تازه وارد کردم getUsers در فایل تست کار کنید و mockImplementation را به روش زیر دوباره تعریف کنید:

(getUsers as jest.Mock).mockImplementation(() => { 
  return Promise.resolve(MOCKED_USERS)
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تنها مشکل این است که … به خوبی کار نمی کند!

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

jest.mock('stores/store.context', () => {
  const getUsersMock = jest.fn();

  return {
    useStore: () => {
        usersStore: {
          getUsers: getUsersMock
        }
    }
  }
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بیایید نگاهی بیندازیم که بعد از همه تغییرات چه چیزی در آزمایش خواهیم داشت

it('triggers getUsers when component is mounted', async () => {
  const {
    usersStore: { getUsers }
  } = useStore();

  (getUsers as jest.Mock).mockImplementation(() => { 
    return Promise.resolve(MOCKED_USERS)
  });

  render(<UsersList />);

  await waitFor(() => expect(screen.getByTestId(Id.UsersListContent)).toBeVisible());
  expect(getUsers).toBeCalled();
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تست قلاب ها و وابستگی های خارجی

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

import { useAuth0 } from '@auth0/auth0-react';
import TreeView from 'components/TreeView';
import { notification } from 'antd';

const SecurePage = observer(({ children }: SecurePageProps) => {
  const { isAuthenticated, user } = useAuth0();
  const onError = () => notification.open(
    type: 'error',
    message: 'Something went wrong'
  });
  ...
  return (
    <div>{isAuthenticated ? children : <LoginPage onError={onError} />}
    <TreeView />
    </div>
  );
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

jest.mock('@auth0/auth0-react');
...
(useAuth0 as jest.Mock).mockImplementation(() => ({
  isAuthenticated: true,
  user: {}
}))
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بنابراین، ما هر دو مورد را که کاربر مجاز است و غیر مجاز را پوشش می دهیم. اما احتمالاً مایلیم نمایش اعلان خطا را نیز آزمایش کنیم، بنابراین مسخره خواهم کرد antd و تایید کنید notification.open اجرا می شود:

import { notification } from 'antd';
...
jest.mock('antd');
...
expect(notification.open).toBeCalled();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

یکی دیگر از مواردی که ممکن است در برخی موارد مفید باشد، تمسخر اجزا است. من دارم TreeView در SecurePage جزء. فرض کنید من نمی خواهم آن را آزمایش کنم، می توانم آن را به روش زیر مسخره کنم:

jest.mock('components/TreeView', () => null)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مواظب باشید از تست رد شدیم TreeView جزء به طور کامل من فقط می توانم آن را توصیه کنم زمانی که TreeView مؤلفه قبلاً به طور جداگانه آزمایش شده است یا زمانی که دلیل خاصی برای حذف آن از آزمایش های خاص دارید.

نتیجه

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

نوشته های مشابه

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

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا