برنامه نویسی

مسخره کردن یا مسخره نکردن مسئله این است

TLTR

تمسخر اغلب به عنوان یک انتخاب پیش فرض در هنگام نوشتن تست ها دیده می شود. با این حال ممکن است پیچیدگی غیر ضروری را به سیستم شما وارد کند. روش های دیگری برای مدیریت وابستگی ها در تست ها وجود دارد.

بیشتر

تمسخر چیست؟

تمسخر – ایجاد اشیایی که رفتار اشیاء واقعی را شبیه سازی می کنند.

در اینجا به طور خلاصه چگونه در سی شارپ تمسخر آمیز به نظر می رسد (JustMock کتابخانه):

// Instantiate a new mock
var mockContactRepository = Mock.Create<IContactRepository>();

// Set up the mock and it's return value 
Mock.Arrange(() => mockContactRepository.GetContacts())
  .Returns(new List<Contact>
  {
      new Contact { ContactId = 1 }, new Contact { ContactId = 2 }
  });

// Pass mock as a dependency
var contactManager = new ContactManager(mockContactRepository);
وارد حالت تمام صفحه شوید

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

اگرچه بسیار مفید به نظر می رسد، اما باید با کمی نمک مصرف شود.

کاستی های تمسخر

1. زمان اجرا به جای بازخورد زمان کامپایل در مورد تغییرات

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

public class CarsControllerTests
{

    [Fact]
    public async void testCreateCar()
    {
        var repositoryMock = new Mock<ICarRepository>();

        // No return value set
        repositoryMock.Setup(r => r.Create(car));

        var carsController = new CarsController(repositoryMock.Object);
        var car = new Car() { Name = "BMW", Available = true };
        var result = await controller.Create(car);

        // Use return value on result
        Assert.Equal("BMW", result.Name);
    }
}
وارد حالت تمام صفحه شوید

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

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

>> dotnet test

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:01.0309627]     Cars.Tests.CarsControllerTests.testCreateCar [FAIL]
  Failed Cars.Tests.CarsControllerTests.testCreateCar [94 ms]
  Error Message:
   System.NullReferenceException : Object reference not set to an instance of an object.
  Stack Trace:
     at Cars.CarsController.Create(Car car) in /Users/kondrashov/Projects/unit-testing-mocking/src/Cars/Controllers/CarController.cs:line 20
   at Cars.Tests.CarsControllerTests.testCreateCar() in /Users/kondrashov/Projects/unit-testing-mocking/test/Cars.Tests/CarsControllerTests.cs:line 20
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state)
Failed!  - Failed:     1, Passed:     0, Skipped:     0, Total:     1, Duration: < 1 ms - Cars.Tests.dll (net7.0)
وارد حالت تمام صفحه شوید

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

ما یک را دریافت می کنیم null reference استثنا در بازگشت car.Id.ToString() برای خوشحال کردن تست باید از آن استفاده کنیم ReturnsAsync() روشی که روی ماست:

// Set return value
repositoryMock.Setup(r => r.Create(car))
    .ReturnsAsync(new Car { Id = "1", Name = "BMW", Available = true });
وارد حالت تمام صفحه شوید

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

به نظر می رسد تغییر ماک بالا آسان و ساده باشد. اما روش های پیچیده تر کمتر پیش پا افتاده می شوند. ارزشی که این تست ارائه می دهد با تمام زمانی که برای نگهداری آن صرف می کنیم کمتر می شود.

ما می خواهیم بدانیم که چه زمانی چیزی در زمان کامپایل خراب شد، به جای زمان اجرا.

2. اتصال محکم به قراردادها

تمسخر ذاتاً با قراردادها همراه است. این کار هر بازسازی را سخت‌تر می‌کند، زیرا شما باید همه مدل‌های واقعی را تغییر دهید.

در زیر من حدود 6 سرویس مختلف در تمسخر دارم. بازسازی قراردادهای خدمات زیر منجر به شکستن تمام این مسخره‌ها می‌شود. شکستگی ها را در تعداد فایل های آزمایشی که در آن ماک ها را تنظیم می کنید ضرب کنید.

راه اندازی ماکت پیچیده

3. سیستم کوتاه تحت آزمایش

هنگامی که یک وابستگی را مسخره می کنیم، سیستم تحت آزمایش (SUT) کوتاهتر است:

سیستم در حال تست در تست های واحد

یک جایگزین خوب برای چنین آزمایشی، یک تست یکپارچه سازی با SUT طولانی تر است:

System Under Test در تست های یکپارچه سازی

4. راه اندازی طولانی باد در هر آزمون

یک چارچوب آزمایشی معمولاً به شما این امکان را می دهد که تنظیمات خود را گروه بندی کنید. با این حال، همیشه اینطور نیست زیرا هر آزمایش اغلب به یک تنظیم اختصاصی نیاز دارد. در زیر مثالی از چگونگی نیاز به کد برای هر آزمون برای راه اندازی mocking آورده شده است:

[TestClass]
public class MyTestClass
{
    private Mock<IMyInterface> _mock;

    [TestInitialize]
    public void Setup()
    {
        _mock = new Mock<IMyInterface>();
    }

    [TestMethod]
    public void Test1()
    {
        _mock.Setup(m => m.MyMethod()).Returns("Hello");
        var result = _mock.Object.MyMethod();
        Assert.AreEqual("Hello", result);
    }

    [TestMethod]
    public void Test2()
    {
        _mock.Setup(m => m.MyMethod()).Returns("World");
        _mock.Setup(m => m.AnotherMethod()).Returns(42);
        var result1 = _mock.Object.MyMethod();
        var result2 = _mock.Object.AnotherMethod();
        Assert.AreEqual("World", result1);
        Assert.AreEqual(42, result2);
    }

    [TestMethod]
    public void Test3()
    {
        _mock.Setup(m => m.MyMethod()).Returns("Goodbye");
        var result = _mock.Object.MyMethod();
        Assert.AreEqual("Goodbye", result);
    }
}
وارد حالت تمام صفحه شوید

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

5. شما نمی توانید هر روشی را مسخره کنید

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

when(spy, method(CodeWaithPrivateMethod.class, "doTheGamble", String.class, int.class))
    .withArguments(anyString(), anyInt())
    .thenReturn(true);
وارد حالت تمام صفحه شوید

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

به جای تمسخر چه کنم؟

1. آزمون های یکپارچه سازی

یک تست ادغام بنویسید نه تست واحد. ارزش بیشتری برای نوشتن یک آزمون ادغام به جای آزمون واحد با مسخره وجود دارد. با نوشتن تست های کمتر کد بیشتری را پوشش می دهیم.

2. آزمون های پایان به انتها

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

3. خرد

یک قطعه خرد قطعه کوچکی از کد است که در حین آزمایش جای مولفه دیگری را می گیرد. مزیت استفاده از یک خرد این است که نتایج ثابتی را برمی‌گرداند و نوشتن تست را آسان‌تر می‌کند.
اگر به دلیل وابستگی هایی که نمی توانید کنترل کنید نمی توانید یک تست سرتاسر بنویسید – از Stubs استفاده کنید. این شبیه به تمسخر است، اما هنگامی که تغییراتی را در سیستم خود ایجاد می کنید، یک بررسی نوع ارائه می دهد. در زیر نمونه ای از یک خرد ساده برای مخزن خودرو آورده شده است:

public class FakeCarRepository : ICarRepository
{
    public async Task<Car> Create(Car car)
    {
        // Any logic to accomodate for creating a car
        return new Car();
    }

    public async Task<Car> Get(string id)
    {
        // Any logic to accomodate for getting a car
        return new Car();
    }
}
وارد حالت تمام صفحه شوید

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

یکی دیگر از مزایای این تست تمیزتر می شود. تمام تنظیمات در یک فایل جداگانه استخراج می شوند:

[Fact]
public async void testCreateCar()
{
   var car = new Car() { Name = "BMW", Available = true };

   var controller = new CarsController(new FakeCarRepository());
   var createdCar = await controller.Create(car);

   Assert.NotNull(createdCar);
}
وارد حالت تمام صفحه شوید

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

خلاصه

بهترین رویکرد به نیازهای خاص پروژه بستگی دارد. تمام مبادلات بین تمسخر و مسخره نکردن را در نظر بگیرید. جایگزین های دیگری برای تمسخر وجود دارد که ممکن است از آنها لذت ببرید.

منابع

  1. مزایای آزمون ادغام نسبت به آزمون واحد
  2. استفاده از کتابخانه TestContainers هنگام نوشتن تست یکپارچه سازی

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

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

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

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