Блоґ

Home / Думки, нариси, різні випадки

Вправи з ChatGPT — підробка значення IOptions<T>

Вправи з ChatGPT — підробка значення IOptions<T>

середа, 5 квітня 2023 р.

Chat GPT

Comments

IOptions<T> — це доволі потужний патерн, який дозволяє надати методу значення конфігураційних параметрів, використовуючи впровадження залежностей. Він дозволяє підвищити ступінь розділення або послабити пов'язаність між частиною застосунку, де відбувається збір конфігураційних даних, і іншою частиною, яка не має турбуватися про різні конфігураційні джерела, натомість має просто отримати якісь конфігураційні значення і якимось чином їх використати.

Однак підробка значення IOptions<T> в юніт-тестах не є аж такою легкою. Зазвичай дані для опцій являють собою простий об'єкт даних з примітивними властивостями, отже, їх підробка є неможливою, оскільки для підроблення об'єкт має впроваджувати інтерфейс або мати віртуальні чи абстрактні властивості чи методи. І ця обставина є саме тією точкою, з якої я почав обговорення своєї проблеми з Chat GPT, зареєструвавшись в цьому сервісі лише за пару днів до того.

Загалом, перед початком, скажімо так, консультації з Chat GPT, я подумки пройшов весь шлях від постановки задачі до її "вирішення", отже, я не застряг на цій проблемі, я лише хотів з'ясувати, чи не упускаю я чогось і чи нема якогось іншого способу вирішення моєї задачі. То був мій перший досвід взаємодії з цією штукою, а отже, як і в реальному спілкуванні з людиною, я почав із загальниго запитання, а відтак крок за кроком почав ставити дедалі більш детальні питання. Наприкінці розмови я був щонайменше глибоко вражений... Хоча пізніше, проаналізувавши цей епізод, я дійшов певних цікавих висновків.

Слово "вирішення" в попередньому абзаці взято в лапки невипадково. На початку дослідження проблеми в мене було обнадійливе передчуття того, що задача може бути вирішена певним чином, про що я поспілкувався з Chat GPT наприкінці розмови. Я запропонував йому своє бачення, і він погодився з тим, що це реально зробити, навіть запропонував код, який реалізує цей підхід. Проте, як я з'ясував дещо пізніше, ми обидва були неправі щодо цього підходу, однак я — запитував, а він — відповідав :)

Отже, далі йде запис мого спілкування з Chat GPT.

Я:

I'm writing unit tests for c# class method with xunit and moq. The sustem under test class has IOption<T> parameter. T is of record type. How can I properly mock this option parameter to be able to provide different testing data from different test methods?

Chat GPT:
To properly mock the IOption<T> parameter of your C# class method in order to provide different testing data from different test methods, you can use Moq to create a mock object of the IOption<T> interface and set up the Value property of the mock object to return different instances of your record type T for each test method.

Here's an example of how you can set up the IOption<T> mock object in your test methods using Moq:

// Define your record type T
public record MyRecord(string Property1, int Property2);

// Define your system under test class with IOption<T> parameter
public class MyClass
{
    public void MyMethod(IOption<MyRecord> option)
    {
        // Method implementation
    }
}

public class MyTests
{
    [Fact]
    public void TestMethod1()
    {
        // Arrange
        var mockOption = new Mock<IOption<MyRecord>>();
        mockOption.Setup(m => m.Value).Returns(new MyRecord("value1", 1));
        var sut = new MyClass();

        // Act
        sut.MyMethod(mockOption.Object);

        // Assert
        // Add your assertions here
    }

    [Fact]
    public void TestMethod2()
    {
        // Arrange
        var mockOption = new Mock<IOption<MyRecord>>();
        mockOption.Setup(m => m.Value).Returns(new MyRecord("value2", 2));
        var sut = new MyClass();

        // Act
        sut.MyMethod(mockOption.Object);

        // Assert
        // Add your assertions here
    }
}
In this example, we define a record type MyRecord with two properties Property1 and Property2. We then define a MyClass class with a MyMethod method that takes an IOption parameter.

In our test methods, we create a mock object of IOption using Moq and set up the Value property of the mock object to return a different instance of MyRecord for each test method. We then create an instance of MyClass and pass the mock object to the MyMethod method.

By setting up the mock object in this way, we can provide different testing data from different test methods.

 

Навіть після найпершої відповіді я був глибоко вражений. Він надав правильний вихідний код, отримавши опис звичайною людською мовою, навіть попри одрук в слові "system" :) Проте, це запитання було лише відправною точкою, тож я почав уточнювати свої питання дедалі більше.

Я:

The problem is that MyClass expects IOption parameter in the constructor

Chat GPT:
If your MyClass constructor expects an IOption parameter, you can still use Moq to mock the IOption interface in your unit tests. Here's an example of how you can do this:

// Define your record type T
public record MyRecord(string Property1, int Property2);

// Define your system under test class with IOption<T> parameter in the constructor
public class MyClass
{
    private readonly IOption<MyRecord> _option;

    public MyClass(IOption<MyRecord> option)
    {
        _option = option;
    }

    public void MyMethod()
    {
        // Use _option.Value here
    }
}

public class MyTests
{
    [Fact]
    public void TestMethod1()
    {
        // Arrange
        var mockOption = new Mock<IOption<MyRecord>>();
        mockOption.Setup(m => m.Value).Returns(new MyRecord("value1", 1));
        var sut = new MyClass(mockOption.Object);

        // Act
        sut.MyMethod();

        // Assert
        // Add your assertions here
    }

    [Fact]
    public void TestMethod2()
    {
        // Arrange
        var mockOption = new Mock<IOption<MyRecord>>();
        mockOption.Setup(m => m.Value).Returns(new MyRecord("value2", 2));
        var sut = new MyClass(mockOption.Object);

        // Act
        sut.MyMethod();

        // Assert
        // Add your assertions here
    }
}
In this example, we define a MyClass class with a constructor that takes an IOption parameter. We also define a MyMethod method that

 

Моє завзяття дедалі збільшувалося...

Я:

Private field _option in MyClasss is not of type IOption<MyRecord>, but MyRecord. It gets its value like _option = option.Value

Chat GPT:
Thank you for the clarification. If the private field _option in MyClass is of type MyRecord and it gets its value like _option = option.Value, you can still use Moq to mock the IOption interface and set up the Value property to return a different instance of MyRecord for each test method.

// Define your record type T
public record MyRecord(string Property1, int Property2);

// Define your system under test class with a private field of type MyRecord
public class MyClass
{
    private MyRecord _option;

    public MyClass(IOption<MyRecord> option)
    {
        _option = option.Value;
    }

    public void MyMethod()
    {
        // Use _option here
    }
}

public class MyTests
{
    [Fact]
    public void TestMethod1()
    {
        // Arrange
        var mockOption = new Mock<IOption<MyRecord>>();
        mockOption.Setup(m => m.Value).Returns(new MyRecord("value1", 1));
        var sut = new MyClass(mockOption.Object);

        // Act
        sut.MyMethod();

        // Assert
        // Add your assertions here
    }

    [Fact]
    public void TestMethod2()
    {
        // Arrange
        var mockOption = new Mock<IOption<MyRecord>>();
        mockOption.Setup(m => m.Value).Returns(new MyRecord("value2", 2));
        var sut = new MyClass(mockOption.Object);

        // Act
        sut.MyMethod();

        // Assert
        // Add your assertions here
    }
}
In this updated example, we create a mock object of IOption using Moq and set up the Value property of the mock object to return a different instance of MyRecord for each test method. We then create an instance of MyClass and pass the mock object to the constructor. In the constructor, we set the private field _option to the value of option.Value.

By setting up the mock object in this way, we can provide different testing data from different test methods, and use the private field _option in our tests.

 

Він навіть оновлює коментарі у вихідному коді! В передостанньому зразку він використовував

public void MyMethod()
{
    // Use _option.Value here
}

коли після мого коментаря він почав писати

public void MyMethod()
{
    // Use _option here
}
Я:

I prefer using constructor in my testing class to have common arrangement setup there. Is there any way to prepare some IOption mock in the test class constructor and provide actual MyRecord data record in testing method?

Chat GPT:
Yes, you can create a mock IOption object in the test class constructor and then set up the Value property of the mock object to return a different instance of MyRecord in each test method. Here's an example:

// Define your record type T
public record MyRecord(string Property1, int Property2);

// Define your system under test class with a private field of type MyRecord
public class MyClass
{
    private MyRecord _option;

    public MyClass(IOption<MyRecord> option)
    {
        _option = option.Value;
    }

    public void MyMethod()
    {
        // Use _option here
    }
}

public class MyTests
{
    private readonly Mock<IOption<MyRecord>> _mockOption;

    public MyTests()
    {
        // Create a mock IOption<MyRecord> object in the constructor
        _mockOption = new Mock<IOption<MyRecord>>();
    }

    [Fact]
    public void TestMethod1()
    {
        // Arrange
        _mockOption.Setup(m => m.Value).Returns(new MyRecord("value1", 1));
        var sut = new MyClass(_mockOption.Object);

        // Act
        sut.MyMethod();

        // Assert
        // Add your assertions here
    }

    [Fact]
    public void TestMethod2()
    {
        // Arrange
        _mockOption.Setup(m => m.Value).Returns(new MyRecord("value2", 2));
        var sut = new MyClass(_mockOption.Object);

        // Act
        sut.MyMethod();

        // Assert
        // Add your assertions here
    }
}
In this example, we create a mock IOption object in the constructor of the MyTests class and store it in a private field _mockOption. We then set up the Value property of the mock object to return a different instance of MyRecord in each test method.

In each test method, we create an instance of MyClass and pass the mock object to the constructor. We then set up the Value property of the mock object with the desired MyRecord instance and call the MyMethod method of the MyClass object. Finally, we add our assertions to check the expected behavior of the system under test.

 

Ось так! Тепер тестовий клас має конструктора.

Я:

Is there any way to have sut initialization in the test class constructor, passing to it some kind of mocked argument, and prepare actual mock in the testing method?

Chat GPT:
Yes, you can initialize the system under test in the constructor of the test class and pass it a mocked argument, and then set up the mock object in each test method with the desired behavior. Here's an example:

// Define your record type T
public record MyRecord(string Property1, int Property2);

// Define your system under test class with a private field of type MyRecord
public class MyClass
{
    private MyRecord _option;

    public MyClass(IOption<MyRecord> option)
    {
        _option = option.Value;
    }

    public void MyMethod()
    {
        // Use _option here
    }
}

public class MyTests
{
    private readonly MyClass _sut;
    private readonly Mock<IOption<MyRecord>> _mockOption;

    public MyTests()
    {
        // Create a mock IOption<MyRecord> object and pass it to the constructor of the system under test
        _mockOption = new Mock<IOption<MyRecord>>();
        _sut = new MyClass(_mockOption.Object);
    }

    [Fact]
    public void TestMethod1()
    {
        // Arrange
        _mockOption.Setup(m => m.Value).Returns(new MyRecord("value1", 1));

        // Act
        _sut.MyMethod();

        // Assert
        // Add your assertions here
    }

    [Fact]
    public void TestMethod2()
    {
        // Arrange
        _mockOption.Setup(m => m.Value).Returns(new MyRecord("value2", 2));

        // Act
        _sut.MyMethod();

        // Assert
        // Add your assertions here
    }
}
In this example, we create an instance of the system under test MyClass in the constructor of the MyTests class and pass it a mocked IOption<MyRecord> object. We then store the system under test in a private field _sut and the mock object in a private field _mockOption.

In each test method, we set up the Value property of the mock object with the desired MyRecord instance and call the MyMethod method of the system under test _sut. Finally, we add our assertions to check the expected behavior of the system under test.

This way, you can reuse the same instance of the system under test for all test methods and set up the mock object with different behavior in each test method.

 

Він знає, що "sut" значає "system under test"... Я майже був зачарований всим цим :)

Я:

In this case field _option will be initialized with null value, since constructor of MyClass will be called before actual mock will be configured in test class. Value of field _option is used in MyMethod and will throw NRE during test execution.

Chat GPT:
You are correct, my apologies for the mistake in my previous answer. In that case, you can pass a factory method to the constructor of MyClass instead of IOption<MyRecord> directly. This factory method can be used to create a new instance of IOption<MyRecord> with the desired MyRecord object. Here's an updated example:

// Define your record type T
public record MyRecord(string Property1, int Property2);

// Define your system under test class with a private field of type MyRecord
public class MyClass
{
    private MyRecord _option;

    public MyClass(Func<MyRecord, IOption<MyRecord>> optionFactory, MyRecord record)
    {
        var option = optionFactory(record);
        _option = option.Value;
    }

    public void MyMethod()
    {
        // Use _option here
    }
}

public class MyTests
{
    private readonly MyClass _sut;

    public MyTests()
    {
        // Create a factory method that returns a mocked IOption<MyRecord> object
        Func<MyRecord, IOption<MyRecord>> optionFactory = record =>
        {
            var mockOption = new Mock<IOption<MyRecord>>();
            mockOption.Setup(m => m.Value).Returns(record);
            return mockOption.Object;
        };

        // Create an instance of the system under test with the factory method and a MyRecord object
        var myRecord = new MyRecord("value1", 1);
        _sut = new MyClass(optionFactory, myRecord);
    }

    [Fact]
    public void TestMethod1()
    {
        // Arrange
        var myRecord = new MyRecord("value2", 2);

        // Act
        _sut.MyMethod();

        // Assert
        // Add your assertions here
    }

    [Fact]
    public void TestMethod2()
    {
        // Arrange
        var myRecord = new MyRecord("value3", 3);

        // Act
        _sut.MyMethod();

        // Assert
        // Add your assertions here
    }
}
In this example, we create an instance of the system under test MyClass in the constructor of the MyTests class and pass it a factory method optionFactory and a MyRecord object. The optionFactory creates a new instance of IOption<MyRecord> with the desired MyRecord object and returns it.

In each test method, we create a new MyRecord object with the desired values and call the MyMethod method of the system under test _sut. Finally, we add our assertions to check the expected behavior of the system under test.

This way, you can reuse the same instance of the system under test for all test methods and create a new MyRecord object with the desired values in each test method.

 

Він знає що таке NRE... Він перепрошує за неправильну пораду! Він пропонує правильний обхідний шлях!

Я:

The thing is that I'm going to obtain options parameter in MyClass through the DI, so I doubt if it is possible to configure DI for factory method optionFactory

Chat GPT:
In that case, you can create a new interface that inherits from IOption<MyRecord> and add a factory method to it. Then you can inject this interface into your MyClass constructor instead of IOption<MyRecord>. This way, you can configure the DI container to use the implementation of the new interface that has the factory method, and you can provide the desired MyRecord object in each test method. Here's an example:

// Define your record type T
public record MyRecord(string Property1, int Property2);

// Define a new interface that inherits from IOption<MyRecord> and adds a factory method
public interface IMyOption : IOption<MyRecord>
{
    IOption<MyRecord> Create(MyRecord record);
}

// Define an implementation of IMyOption that has the factory method
public class MyOption : IMyOption
{
    private readonly IOption<MyRecord> _option;

    public MyOption(MyRecord record)
    {
        _option = Options.Create(record);
    }

    public MyRecord Value => _option.Value;

    public IOption<MyRecord> Create(MyRecord record)
    {
        return Options.Create(record);
    }
}

// Define your system under test class with a private field of type MyRecord
public class MyClass
{
    private MyRecord _option;

    public MyClass(IMyOption option, MyRecord record)
    {
        var newOption = option.Create(record);
        _option = newOption.Value;
    }

    public void MyMethod()
    {
        // Use _option here
    }
}

public class MyTests
{
    private readonly MyClass _sut;
    private readonly IMyOption _option;

    public MyTests()
    {
        // Configure the DI container to use the implementation of IMyOption that has the factory method
        var services = new ServiceCollection();
        services.AddTransient<IMyOption, MyOption>();
        var serviceProvider = services.BuildServiceProvider();

        // Create an instance of the system under test with the injected IMyOption and a null MyRecord object
        _option = serviceProvider.GetService<IMyOption>();
        _sut = new MyClass(_option, null);
    }

    [Fact]
    public void TestMethod1()
    {
        // Arrange
        var myRecord = new MyRecord("value1", 1);
        var newOption = _option.Create(myRecord);
        _sut.GetType().GetField("_option", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(_sut, newOption.Value);

        // Act
        _sut.MyMethod();

        // Assert
        // Add your assertions here
    }

    [Fact]
    public void TestMethod2()
    {
        // Arrange
        var myRecord = new MyRecord("value2", 2);
        var newOption = _option.Create(myRecord);
        _sut.GetType().GetField("_option", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(_sut, newOption.Value);

        // Act
        _sut.MyMethod();

        // Assert
        // Add your assertions here
    }
}
In this example, we define a new interface IMyOption that inherits from IOption<MyRecord> and adds a factory method Create. We also define an implementation of IMyOption called MyOption that has the Create method.

In the constructor of MyClass, we inject IMyOption instead of IOption<MyRecord>. We also modify the constructor of MyClass to use the Create method of IMyOption to create a new instance of IOption<MyRecord> with the desired MyRecord object.

In the constructor of MyTests, we configure the DI

 

Він просто говорить моєю мовою! Не треба пояснювати, що значить DI. Це неперевершено! Поки що...

Я:

Ok, but this makes the whole approach a bit complicated for my particular case. What if I will have private field of MyRecord type in the testing class and will prepare mock that will return this field every time? And each test method will have responsibility to prepare concrete values for this field before calling sut? What do you think about such solution?

Chat GPT:
Yes, that's also a valid approach. You can create a private field of type MyRecord in your test class and prepare a mock of IOption<MyRecord> that always returns this field. Then in each test method, you can set the value of the MyRecord field to the desired value before calling the system under test.
    
Here's an example of how you can implement this approach:    

public class MyClass
{
    private readonly MyRecord _option;

    public MyClass(IOption<MyRecord> option)
    {
        _option = option.Value;
    }

    public void MyMethod()
    {
        // Use _option here
    }
}

public class MyTests
{
    private readonly MyClass _sut;
    private readonly IOption<MyRecord> _option;

    // Create a private field of type MyRecord in the test class
    private MyRecord _myRecord;

    public MyTests()
    {
        // Create a mock of IOption<MyRecord> that always returns the _myRecord field
        var mockOption = new Mock<IOption<MyRecord>>();
        mockOption.SetupGet(o => o.Value).Returns(() => _myRecord);
        _option = mockOption.Object;

        // Create an instance of the system under test with the injected IOption<MyRecord>
        _sut = new MyClass(_option);
    }

    [Fact]
    public void TestMethod1()
    {
        // Arrange
        _myRecord = new MyRecord("value1", 1);

        // Act
        _sut.MyMethod();

        // Assert
        // Add your assertions here
    }

    [Fact]
    public void TestMethod2()
    {
        // Arrange
        _myRecord = new MyRecord("value2", 2);

        // Act
        _sut.MyMethod();

        // Assert
        // Add your assertions here
    }
}
In this example, we create a private field _myRecord of type MyRecord in the MyTests class. We also create a mock of IOption<MyRecord> that always returns _myRecord.

In the constructor of MyTests, we set the _option field to the mock of IOption<MyRecord> that we created earlier.

In each test method, we set the _myRecord field to the desired value before calling the system under test.

This approach is simpler than the previous one as it doesn't require creating a new interface and implementation of it. It also allows you to have a common arrangement setup in the test class constructor, while still being able to provide different testing data from different test methods.

 

Аж ось ми втрапили в пастку. Я усвідомив це хвилин за 10, поки пишався тим, що аж ціла ШІ-штука схвалила мою ідею! Проте, намагаючись її реалізувати, я з'ясував, що поле _option все ж таки ініціалізується null-значенням, отже, мети не досягнуто аж ніяк...

Я:

I'm facing the same issue. When MyClass constructor is called, _options gets value of null. Later on, during test method execution, inside MyMethod _options still has null, so I have NRE.

Chat GPT:
If you are still facing the issue where the _option field in MyClass is null, even though you have set up the mock correctly, it is possible that the dependency injection container is not configured correctly.

Make sure that you have registered the IOption<MyRecord> interface with the dependency injection container and that it is correctly injected into the MyClass constructor.

Here's an example of how you can register the IOption<MyRecord> interface with the .NET Core dependency injection container:

// In your startup class or the composition root of your application
services.Configure<MyRecordOptions>(Configuration.GetSection("MyRecordOptions"));
services.AddScoped<IOption<MyRecord>>(provider =>
    Options.Create(provider.GetService<MyRecordOptions>())
);
This code registers the MyRecordOptions class as a configuration object with the Configuration.GetSection method. It then registers the IOption<MyRecord> interface as a scoped service with the Options.Create method, which creates an instance of MyRecordOptions using the configuration settings and returns it as an IOption<MyRecord> object.

Make sure that you have also added the MyClass class as a service in the dependency injection container and that it has the IOption<MyRecord> parameter in its constructor.

// In your startup class or the composition root of your application
services.AddScoped<MyClass>();
If you have already registered the interface and the class correctly, make sure that you are using the correct instance of the dependency injection container in your test class. You can create a separate instance of the dependency injection container in your test class and register the IOption<MyRecord> mock with it.

public class MyTests
{
    private readonly IOption<MyRecord> _option;
    private readonly MyClass _sut;

    public MyTests()
    {
        // Create a mock of IOption<MyRecord> and register it with the test container
        var mockOption = new Mock<IOption<MyRecord>>();
        // Set up the mock as required
        _option = mockOption.Object;

        // Create a new instance of the dependency injection container for the test
        var services = new ServiceCollection();
        services.AddScoped(provider => _option);
        services.AddScoped<MyClass>();
        var serviceProvider = services.BuildServiceProvider();

        // Resolve the system under test using the test container
        _sut = serviceProvider.GetService<MyClass>();
    }

    // Tests go here...
}
By creating a new instance of the dependency injection container and registering the mock with it, you can ensure that the correct instance of IOption<MyRecord> is injected into the MyClass constructor when the system under test is resolved.

 

Як видно, з цього моменту Chat GPT почав втрачати нитку розмови. Можливо, я дістався обмеження безкоштовної версії Chat GPT 3 стосовно обсягу пам'яті для окремої розмови, однак я почав отримувати невідповідні відповіді.

Просто аби перевірити свою здогадку, я поставив ще одне питання.

Я:

I have NRE during test execution, not during normal run.

Chat GPT:
If you are getting a NullReferenceException (NRE) during test execution, it's possible that the mock object you created is not being injected into the MyClass constructor during the test execution.

Here are a few things you can try to resolve the issue:

Verify that the mock object is being created correctly and has the expected behavior. You can do this by adding a breakpoint in your test method and inspecting the mock object. Make sure that the mock object has the expected behavior when the Value property is accessed.

Verify that the mock object is being registered correctly with the dependency injection container. You can do this by adding a breakpoint in the test class constructor and verifying that the mock object is being injected into the MyClass constructor. If the mock object is not being injected, then there may be an issue with the DI configuration.

Verify that the MyClass constructor is being called with the mock object during the test execution. You can do this by adding a breakpoint in the MyClass constructor and verifying that the _option field is being initialized correctly. If the _option field is null, then there may be an issue with the DI configuration or the MyClass implementation.

Try changing the lifetime of the mock object to Scoped or Transient. Sometimes, if the lifetime of the mock object is not set correctly, it can cause unexpected behavior during the test execution.

If you have already tried these steps and are still getting the NRE, then it's possible that there is a bug in your code that needs to be investigated further.

 

Вочевидь, Chat GPT таки втомився :) 

Я:

Can I somehow save our conversation to my storage?

Chat GPT:
Unfortunately, there is no direct way to save our conversation to your storage from within the chat window. However, you can copy and paste the conversation into a document or a text file to save it on your local storage.

Alternatively, you can take a screenshot of the chat window and save it as an image file. Most operating systems have built-in tools to take screenshots.

 

Отже, якої думки щодо цього спілкування я дійшов?

По-перше, як сказав Парубій після інавгурації Зеленського, "це було весело". Я говорив (друкував) у спосіб, дуже близький до того, як я думаю, маючи спілкування практично в режимі "сеньйор-сеньйор".

По-друге, я вбачаю в подібних інструментах величезний потенціал. Якщо цю штуку вдосконалити ще більше, вона дійсно може скасувати необхідність гуглити в багатьох випадках, якщо предмет пошуку не є чимось, що з'явилося нещодавно.

Отже, я можу сказати напевно, що поточна інкарнація цієї штуки в цьому каверзному випадку в технічному сенсі працює десь так само, як і Roslyn, Resharper чи якийсь інший аналізатор коду. Жоден з них досі не має достатньо потужних евристик, аби передбачити NRE в цьому коді під час виконання, роблячи попередній статичний аналіз коду. Код, запропонований Chat GPT, зкомпілюється, але це означатиме, що помилка виникне в якийсь невідомий момент вже під час виконання, що буде навіть гірше. Можу лише уявити собі розпач якогось безтурботного джуніора, який радісно покладатиметься на код Chat GPT і намагатиметься запускати реальні програми для більш-менш складних задач.

Чи мали ви подібний досвід з Chat GPT? Поділіться думками в коментарях!

© theyur.dev. All Rights Reserved. Designed by HTML Codex