<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های محمد سعید صدیقی</title>
        <link>https://virgool.io/feed/@m_32687925</link>
        <description></description>
        <language>fa</language>
        <pubDate>2026-06-16 09:17:42</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/3387533/avatar/P6ICpM.jpg?height=120&amp;width=120</url>
            <title>محمد سعید صدیقی</title>
            <link>https://virgool.io/@m_32687925</link>
        </image>

                    <item>
                <title>قابل‌اعتماد کردن Monkey Patching در تست‌های Go</title>
                <link>https://virgool.io/@m_32687925/%D9%82%D8%A7%D8%A8%D9%84-%D8%A7%D8%B9%D8%AA%D9%85%D8%A7%D8%AF-%DA%A9%D8%B1%D8%AF%D9%86-monkey-patching-%D8%AF%D8%B1-%D8%AA%D8%B3%D8%AA-%D9%87%D8%A7%DB%8C-go-mkotmjhdovbn</link>
                <description>غیرفعال کردن بهینه‌سازی‌های کامپایلر برای کار کردن درست gomonkeyمقدمهتست نوشتن یکی از مهم‌ترین بخش‌های توسعه نرم‌افزار است. تست‌ها کمک می‌کنند باگ‌ها را زودتر پیدا کنیم، رفتارهای غیرمنتظره را کنترل کنیم و با خیال راحت‌تر تغییرات جدید اضافه کنیم.در Go به صورت پیش‌فرض پکیج testing وجود دارد و علاوه بر آن ابزارهای مختلفی مثل test suiteها، mocking و patching داریم که نوشتن تست را راحت‌تر می‌کنند. این ابزارها از نظر ایده شبیه چیزی هستند که در زبان‌های دیگر هم می‌بینیم؛ مثلا در Java ابزارهایی مثل JUnit و Mockito داریم و در Python هم PyTest و unittest.mock.مدتی پیش وقتی روی یک پروژه کار می‌کردم و در حال یادگیری عمیق‌تر تست‌نویسی در Go و ابزارهایی مثل monkey patching بودم، با مشکلی برخورد کردم که باعث می‌شد تست‌ها بعضی وقت‌ها کار کنند و بعضی وقت‌ها نه. بعد از کمی بررسی فهمیدم که بهینه‌سازی‌های کامپایلر دلیل اصلی این رفتار هستند. در این مقاله می‌خواهم مشکل و راه‌حل آن را به زبان ساده توضیح بدهم تا شما وقت‌تان را برای پیدا کردن راه حل همین مشکل هدر ندهید.Patching و Monkey Patching چیست؟گاهی در تست‌ها لازم است یک تابع یا متد را موقتا با یک نسخه‌ی ساختگی (mock) جایگزین کنیم. این کار کمک می‌کند:خطاها را شبیه‌سازی کنیممسیرهای خاصی از کد اجرا شوندوابستگی‌هایی مثل دیتابیس یا شبکه را حذف کنیمcoverage را بالا ببریمبه این تکنیک monkey patching گفته می‌شود.به زبان ساده:monkey patching یعنی جایگزین کردن موقت یک تابع یا متد با نسخه‌ی mock فقط در زمان تست.مثلا:func GetTime() int64 {
    return time.Now().Unix()
}
در تست می‌توانیم آن را این‌طور عوض کنیم:patches := gomonkey.ApplyFunc(GetTime, func() int64 {
    return 1234567890
})
defer patches.Reset()
از این لحظه داخل تست، هر بار GetTime() صدا زده شود، مقدار ثابت برمی‌گرداند.نکته مهم: فقط برای تستMonkey patching اصلا مناسب production نیست.این روش:thread-safe نیسترفتار قابل پیش‌بینی نداردوابسته به جزئیات داخلی runtime استفقط برای تست طراحی شدهپس هرگز در کد اصلی برنامه از آن استفاده نکنید.کتابخانه‌های Monkey Patching در Goدو کتابخانه‌ی معروف وجود دارد:bou.ke/monkeyagiledragon/gomonkeyهر دو با دستکاری pointer توابع در runtime کار می‌کنند.در عمل، gomonkey گزینه‌ی بهتری است چون:با نسخه‌های جدید Go سازگارتر استپایدارتر استAPI ساده‌تری داردنصب:go get github.com/agiledragon/gomonkey/v2
استفاده‌ی ساده از gomonkeyPatch کردن یک تابعpatches := gomonkey.ApplyFunc(Add, func(a, b int) int {
    return 42
})
defer patches.Reset()
Patch کردن یک متدpatches := gomonkey.ApplyMethod(
    &amp;UserService{},
    &quot;FetchUser&quot;,
    func(_ *UserService, id int) (*User, error) {
        return nil, errors.New(&quot;mock error&quot;)
    },
)
defer patches.Reset()
حتما Reset کنیدخیلی مهم است:defer patches.Reset()
اگر reset نکنید، patch ممکن است روی تست‌های بعدی هم اثر بگذارد و خطاهای عجیب ایجاد کند.مشکل اصلی: چرا گاهی Patch کار نمی‌کند؟ممکن است با این حالت‌ها روبه‌رو شوید:patch اعمال نمی‌شودتابع اصلی اجرا می‌شودتست‌ها بعضی وقت‌ها fail می‌شوندروی یک سیستم کار می‌کند ولی روی سیستم دیگر نهدلیل اصلی: بهینه‌سازی‌های کامپایلر Goکامپایلر Go از روش های زیادی برای optimize می‌کند. مثلا:inliningحذف کدهای اضافیreorder کردن دستوراتحذف سمبل‌هااگر یک تابع inline شود، دیگر به عنوان یک تابع مستقل وجود ندارد. یعنی چیزی برای patch کردن باقی نمی‌ماند.مثلا:func Add(a, b int) int {
    return a + b
}
کامپایلر ممکن است این را مستقیم داخل caller کپی کند. در این حالت patch کردن Add هیچ اثری ندارد.چرا در حالت Debug کار می‌کند؟ممکن است دیده باشید:وقتی با breakpoint یا debugger اجرا می‌کنید، patch کار می‌کند.دلیلش این است که در حالت debug معمولا بهینه‌سازی‌ها غیرفعال می‌شوند. بنابراین تابع inline نمی‌شود و patch درست کار می‌کند.ولی در اجرای عادی تست‌ها، optimize فعال است و patch خراب می‌شود.راه‌حل: غیرفعال کردن بهینه‌سازی در زمان تستراه‌حل ساده است:موقع اجرای تست‌ها optimization و inlining را غیرفعال کنید.با gcflags:go test -gcflags=&quot;all=-N -l&quot;
معنی فلگ‌ها-N → غیرفعال کردن optimization-l → غیرفعال کردن inliningall= → برای همه‌ی پکیج‌ها اعمال شود (خیلی مهم)اگر all= را نگذارید، بعضی پکیج‌ها هنوز optimize می‌شوند و patch ممکن است باز هم fail شود.نکته کاربردیمی‌توانید alias بسازید:alias gotest=&#039;go test -gcflags=&quot;all=-N -l&quot; -count=1&#039;
و بعد:gotest ./...
-count=1 جلوی cache شدن تست‌ها را می‌گیرد.چه زمانی از Monkey Patching استفاده کنیم؟فقط وقتی:dependency و legacy داریدrefactor کردن سخت استinterface کافی نیستنیاز به کنترل دقیق اجرای کد داریددر حالت عادی، بهتر است از interface و dependency injection استفاده کنید. monkey patching باید آخرین گزینه باشد.نتیجه‌گیریMonkey patching می‌تواند ابزار قدرتمندی برای تست در Go باشد. کتابخانه‌هایی مثل gomonkey این کار را ساده می‌کنند و کمک می‌کنند مسیرهای خاص را تست کنیم و coverage را بالا ببریم.اما بهینه‌سازی‌های کامپایلر، مخصوصا inlining، می‌توانند باعث شوند patch اصلا اعمال نشود و تست‌ها رفتار عجیب داشته باشند.راه‌حل خیلی ساده است:go test -gcflags=&quot;all=-N -l&quot;
بهینه‌سازی را در زمان تست غیرفعال کنید تا patch قابل‌اعتماد شود.اگر از monkey patching استفاده می‌کنید، همیشه این سه نکته را یادتان باشد:فقط برای تست، حتما reset کنید، و optimization را غیرفعال کنید.</description>
                <category>محمد سعید صدیقی</category>
                <author>محمد سعید صدیقی</author>
                <pubDate>Wed, 04 Feb 2026 22:59:02 +0330</pubDate>
            </item>
            </channel>
</rss>