Philocode – Telegram
Philocode
چند تست برای متد زیر لازم داریم تا Code Coverage صد در صد داشته باشیم؟ و چند تست نیاز داریم تا تمام حالت‌های ممکن را پوشش دهیم؟ public function pay($amount): bool { if ($amount <= 0) { throw new SomeException('Are you serious?'); } return…
وقتشه که این سوال رو جواب بدیم.

چند تست برای متد زیر لازم داریم تا Code Coverage صد در صد داشته باشیم؟
اول باید بفهمیم که code coverage چیه. code coverage به زبان ساده یعنی اینکه وقتی تست رو اجرا کردید، چند خط از کدهای اپلیکیشن از کل خطوط اجرا شدند؟ مثلاً کدی که 100 خط باشه، و با اجرای تست، 50 خطش اجرا بشه، یعنی 50% کدها پوشش داده شده. 50% کدهای ما در حین اجرای تست به کلی اجرا نشدند.
توی نمونۀ ما، دو تست برای اینکه code coverage ما 100% بشه نیازه؛ توی یکی باید داخل if اجرا بشه و توی دومی قسمت مربوط به اجرای متد pay از اون سرویس و جوابی که برمی‌گرده. اگه مقدار amount مثبت باشه، هیچ‌وقت به داخل if نمی‌رسیم و در نتیجه اون قسمت پوشش داده نخواهد شد.

چند تست نیاز داریم تا تمام حالت‌های ممکن را پوشش دهیم؟
ممکنه که code coverage کد ما 100% باشه، ولی لزوماً معنیش این نیست که برای کل حالت‌هایی که ممکنه پیش بیان، تست نوشتیم. توی این مورد، با دو تست تونستیم 100% از code coverage رو به دست بیاریم، ولی توی این کد نه دو حالت، سه حالت وجود داره:
1. وقتی amount مساوی یا کوچک‌تر از صفر باشه.
2. وقتی که متد pay از اون سرویس، true برگردونه.
3. وقتی که اون متد، false برگردونه.
اگه به return type متد pay خودمون دقت کنیم، می‌بینیم که bool هست و این خودش یعنی دو حالت؛ پس جواب این سوال، می‌شه سه تست.
#testing
👍2
Philocode
گفتیم که query methodها نباید side-effect (عوارض جانبی) داشته باشند، برای همین نویسنده یک قاعدۀ دیگه رو معرفی می‌کنه: Query methods should use other query methods, not command methods. به مثال زیر دقت کنید: public function payable(): number { return this.subTotal()…
گفتیم که query methodها نباید از command methodها استفاده کنند. این مصداق واضح سوء استفاده‌ست؛ چون command methodها side-effect دارند و این‌طوری query method رو از چیزی که باید باشه (اینکه ده‌بار کال کنیم و باز هم چیزی تغییر نکنه) منحرف می‌کنند.

اما command methodها می‌تونند از query methodها استفاده کنند، چون برعکس اون قاعده اینجا صادق نیست: command methodها side-effect دارند و کال‌شدن یک query method داخل اونها، چیزی رو خراب نمی‌کنه.
توی نسخۀ فعلی TeleBot می‌شه کیبورد رو chunk کرد تا هر ردیف، به تعدادی که ذکر شده دکمه داشته باشه؛ مثلاً شش‌تا دکمه اضافه کرده و بعد هر ردیف رو سه‌دکمه‌ای می‌کنه:
->chunk(3)
نتیجه:
1️⃣2️⃣3️⃣
4️⃣5️⃣6️⃣

اما ان شا الله توی نسخۀ بعدی، میشه هم int و هم array رو به متد chunk پاس داد:
->chunk([2, 4])
نتیجه:
1️⃣2️⃣
3️⃣4️⃣5️⃣6️⃣

لطف کنید به ریپازیتوری ⭐️ بدید که انگیزۀ بیشتری برای اوپن‌سورس داشته باشم.
👍5
خُب خـــــــــیـــــــــلی بهتر شد! 🔥
#refactoring #telebot
🔥2
تعریف‌کردن کلاس‌ها به صورت final، چرا؟

اگه کلمۀ کلیدی final رو به کلاسی اضافه کنیم، امکان extendکردن اون کلاس وجود نخواهد داشت.

مثال:
<?php
final class A {}
class B extends A {}
خروجی:
PHP Fatal error: Class B may not inherit from final class (A)

می‌فرمایند که composition راه بهتر و منعطف‌تری از inheritance ــــه (همیشه؟)، برای همین وقتی ترجیحمون استفاده از final باشه، راه inheritance رو می‌بندیم و کسانی که با کلاس کار دارند می‌فهمند که باید به جای inheritance و overrideکردن، دنبال راه دیگه‌ای برای تغییر رفتار کلاس باشند و به نحوی اونها رو به اجرای اصل Composition over inheritance سوق می‌دیم.

برای مطالعۀ بیشتر، این لینک‌هارو داشته باشید:
https://matthiasnoback.nl/2018/09/final-classes-by-default-why
https://en.wikipedia.org/wiki/Composition_over_inheritance
https://medium.com/@orhanobut/favor-composition-over-inheritance-a3e5462d01f5
https://stackoverflow.com/questions/32308276/when-to-favor-inheritance-over-composition
👍3
کد بالا، اصطلاحاً tight coupling داره و کد ما شدیداً به کلاس Facebook درهم‌تنیده شده. اگه مثل کد پایین از dependency injection استفاده کنیم، به loose coupling می‌رسیم و خیلی راحت میشه در هنگام تست‌نوشتن، یه mock رو به جای اون سرویس واقعی پاس داد و بقیۀ منطق کلاس رو تست کرد. از طرفی اصل دوم و پنجم SOLID رو رعایت کردیم و می‌تونیم هر سرویس دیگه‌ای مثل Twitter رو بدون تغییردادن این کلاس وارد ماجرا کنیم و انعطاف کلاس Publisher رو حفظ کنیم.

حمله‌ای؟ نظری؟ سوالی؟
👍1
از زیبایی‌های Typenoscript:

interface Todo {
noscript: string;
}

const todo: Readonly<Todo> = {
noscript: "Delete inactive users",
};

todo.noscript = "Hello";

⛔️ Cannot assign to 'noscript' because it is a read-only property.
👍1
Philocode
از زیبایی‌های Typenoscript: interface Todo { noscript: string; } const todo: Readonly<Todo> = { noscript: "Delete inactive users", }; todo.noscript = "Hello"; ⛔️ Cannot assign to 'noscript' because it is a read-only property.
البته توی PHP هم می‌تونیم مثل این رو داشته باشیم، ولی از Utility Typeها خبری نیست و باید داخل خود کلاس تعریف بشه:

class Developer
{
public readonly name;
}

$developer = new Developer();
$developer->name = 'John';

⛔️ Error: Cannot modify readonly property Developer::$name...

اینطوری بیرون از آبجکت نمی‌شه پراپرتی‌هارو تغییر داد.
👍1
Philocode
تعریف‌کردن کلاس‌ها به صورت final، چرا؟ اگه کلمۀ کلیدی final رو به کلاسی اضافه کنیم، امکان extendکردن اون کلاس وجود نخواهد داشت. مثال: <?php final class A {} class B extends A {} خروجی: PHP Fatal error: Class B may not inherit from final class (A) می‌فرمایند…
استثنایی که برای این می‌تونه وجود داشته باشه، دیزاین پترن Template ــــه.

این مثال رو بیبنید:
class SomeJob
{
public function handle()
{
$this->firstStep();
$this->secondStep();
$this->thirdStep();
$this->fourthStep();
}
}
مثلاً اون چهارتا متد، روی همون کلاس SomeJob تعریف شدند و هر کدوم از اونها، مرحله‌ای از انجام کاره؛ مثلاً اولی روغن می‌ریزه، دومی تخم مرغ رو اضافه می‌کنه، سومی نمک رو اضافه می‌کنه و چهارمی هم یه چیز دیگه. Template Pattern می‌گه بذارید که این مراحل قابل overrideکردن باشند، تا بشه بعضی از این مراحل رو توی کلاسی که از این کلاس ارث می‌بره، تغییر داد؛ مثلاً به جای مرحلۀ سوم که نمک می‌ریزه، مرگ موش اضافه کنه. (چه مثال دارکی)

اینجا overrideکردن جزئی از ذات این پترنه و معنی نداره با final از overrideشدنش جلوگیری کنیم!
ساختار لاراول بر اساس Technical Responsibilityهاست، بر خلاف Nest.js که پروژه رو بر حسب ماژول تقسیم می‌کنه و می‌شه بر اساس Domain Modelها پروژه رو پیش برد.
👍1😱1
حتماً دیدید که بعضی از سایت‌ها از markdown استفاده کردند و می‌شه کد زبان‌های مختلفی رو نشون داد:
```php
$sum = $a + $b;
```
ولی شاید این رو ندونید که بعضی جاها از جمله گیت‌هاب، می‌شه به تغییرات (حذف و اضافه) کد با این syntax اشاره کرد:
```diff
- old line
+ new line
```
😱5
Philocode
https://www.youtube.com/watch?v=DuB6UjEsY_Y
می‌دونستید چرا توی PHP متغیرهامون $ دارن؟
ربطی به پول‌دوست‌بودن راسموس نداره؛ صرفاً هدف این بوده که وقتی متغیر رو داخل یه string می‌ذاری، قاطی نشه! 😁
😁4