دیگر ILogger را Mock نکنید: تست لاگینگ در داتنت با FakeLogger
نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۷/۲۴ ۰۹:۳۰
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
ILogger از کتابخانه پایهی لاگینگ مایکروسافت با آن روبرو هستیم.ILogger نسبتاً ساده است، اما شما به ندرت به طور مستقیم با آن تعامل میکنید (مثلاً متد Log آن را مستقیماً فراخوانی نمیکنید، بلکه از LogInformation، LogError و... استفاده میکنید که همگی Extension Method هستند). پس چگونه میخواهید تست کنید که یک متد با پارامترهای درستی فراخوانی شده تا از صحت عملیات لاگینگ خود مطمئن شوید؟ILogger<> را با استفاده از کتابخانههایی مانند Moq ماک کرد و آن را به SUT (System Under Test یا سیستم تحت تست) تزریق کرد و فراخوانیهای آن را بررسی نمود. اما این کار میتواند درگیرکننده و پیچیده شود و همچنین شما مجبور میشوید کدهای بررسی ماک (Mock Verification) خود را بین پروژههای مختلف کپی کنید. این اصلاً ایدهآل نیست.FakeLogger به شما دسترسی کامل به تمام دادههایی را میدهد که در طول اجرای کد شما به ILogger ارسال شدهاند و به شما اجازه میدهد تا در تستهای خود، مقادیر آنها را به سادگی Assert (بررسی) کنید.Microsoft.Extensions.Diagnostics.TestingMicrosoft.Extensions.Logging.TestingDemoService را در نظر بگیرید:public class DemoService(ILogger<DemoService> logger)
{
public void LogMe()
{
logger.LogInformation("This should be logged.");
}
}LogMe اجرا میشود، متد لاگ مناسب (در اینجا LogInformation) فراخوانی شده است.FakeLogger است.// Arrange
var fakeLogger = new FakeLogger<DemoService>();
var sut = new DemoService(fakeLogger);
// Act
sut.LogMe();
// Assert
fakeLogger.LatestRecord.Message.Should().Be("This should be logged.");FakeLogger<> اینترفیس ILogger<> را پیادهسازی میکند، میتوانیم به سادگی نمونهای از آن را به سازندهی DemoService پاس دهیم.LatestRecord در fakeLogger است. این پراپرتی، آخرین فراخوانی به نمونهی لاگر را در سیستم تحت تست افشا میکند.FakeLogger فقط پیام را ضبط نمیکند، بلکه تمام جزئیات لاگ را ثبت میکند. شما به راحتی میتوانید سطح لاگ (Log Level) را نیز بررسی کنید:// Assert (ادامه تست قبلی)
fakeLogger.LatestRecord.Message.Should().Be("This should be logged.");
fakeLogger.LatestRecord.LogLevel.Should().Be(LogLevel.Information);FakeLogger تنها برای پیامهای ساده نیست؛ قدرت واقعی آن زمانی مشخص میشود که بخواهیم Scope ها را تست کنیم.RequestID، CorrelationID یا شناسه یک موجودیت (Entity Identifier) را به یک Scope اضافه کنید. این کار باعث میشود هر لاگی که در آن بلوک کد ثبت میشود، به طور خودکار آن متادیتای زمینهای را به همراه داشته باشد. این قابلیت، فیلتر کردن و درک لاگها را در سیستمهای توزیعشده یا هنگام دیباگ کردن یک درخواست خاص، بسیار آسانتر میکند.public partial class DemoService(ILogger<DemoService> logger)
{
public void LogMe(Guid id)
{
// ایجاد یک اسکوپ با اطلاعات زمینهای
using var _ = logger.BeginScope(new Dictionary<string, object> { { "id", id } });
logger.LogInformation("This should be logged.");
}
}BeginScope یک زمینه (Context) لاگ با یک کلید واحد (id) ایجاد میکند. هنگامی که logger.LogInformation اجرا میشود, Scope به طور خودکار آن دادههای زمینهای را ضمیمه میکند. اما در یک یونیت تست، چگونه میتوانیم تأیید کنیم که Scope واقعاً اعمال شده است؟FakeLogger تمام رکوردهای لاگ، از جمله تمپلیتهای پیام، سطوح لاگ، استثناها و Scopeها را ردیابی میکند. بیایید تست کنیم که DemoService ما به درستی پیام را لاگ کرده و id را به Scope پیوست کرده است:public class DemoServiceTests
{
[Fact]
public void ScopeUsage()
{
// Arrange
var fakeLogger = new FakeLogger<DemoService>();
var sut = new DemoService(fakeLogger);
var id = Guid.NewGuid();
// Act
sut.LogMe(id);
// Assert
// 1. بررسی پیام لاگ
fakeLogger.LatestRecord.Message.Should().Be("This should be logged.");
// 2. بررسی اسکوپ
// Scopes مجموعهای از اسکوپهای فعال در زمان لاگ است
var scope = fakeLogger.LatestRecord.Scopes[0] as Dictionary<string, object>;
scope.Should().NotBeNull();
scope.Keys.Should().Contain("id");
scope.Values.Should().Contain(id);
}
}FakeLogger میسازیم (بدون نیاز به ماک یا Setup).LogMe فراخوانی میشود و یک رکورد لاگ تولید میکند.Scopes ثبت شده را بازرسی میکنیم.object ضبط میشود، و در این مورد، ما میدانیم که آن یک Dictionary است که حاوی id ماست.Dictionary پاس داده شده است). اما برای این نوع تستهای سطح پایین، این میزان از آگاهی قابل قبول است.FakeLogger مایکروسافت، اکنون به سادگی میتوان Assert کرد که پیامهای لاگ، سطوح و Scopeها همگی به درستی اعمال شدهاند، بدون اینکه به کتابخانههای ماکینگ شخص ثالث یا Helperهای تست سفارشی متوسل شویم.FakeLogger جایگزین ماک کردن ILogger میشود.FakeLogger میتواند آن Scopeها را برای تست، ضبط و افشا کند.FakeLogger، میتوانید بر روی تست منطق برنامه و رفتار لاگینگ خود به طور همزمان تمرکز کنید؛ به صورت شفاف، ساده و بدون کدهای تکراری و اضافی (Boilerplate).