Coding With ChatGPL

Share on:

This post is me having a conversation with an AI called ChatGPT, using it as a time saving tool for writing code. I’d been thinking about some code I needed to write in LIFTI. I knew what I wanted it to do and how I was going to implement it, but as an old friend once told me “Just because you know how to do something, doesn’t mean it takes zero time”. So I figured I’d have a bit of fun with ChatGPT.

Hi, would you like to help me write a C# class?


Sure, I’d be happy to help you write a C# class. Do you have any specific requirements or details that you’d like to share?


Great, thanks! This class will be called “VirtualString” it’s constructor will take an array of strings and it will provide a method with the signature “string Substring(int start, int length)” that is able to create a substring from that array. Each element in the array should be considered part of the same overall string. For example, if I passed ay array containing “test”, “string” and “test”, SubString(3, 4) would return “tst”. Give it a go!


Sure, I can help you write a VirtualString class that takes an array of strings and provides a Substring method to create a substring from the array of strings. Here is an example of how the class could be implemented:

 1public class VirtualString
 2{
 3    private string[] _strings;
 4
 5    // constructor that takes an array of strings
 6    public VirtualString(string[] strings)
 7    {
 8        _strings = strings;
 9    }
10
11    // Substring method that creates a substring from the array of strings
12    public string Substring(int start, int length)
13    {
14        // create a new string builder to store the substring
15        StringBuilder sb = new StringBuilder();
16
17        // calculate the total length of the array of strings
18        int totalLength = _strings.Sum(s => s.Length);
19
20        // handle the case where the start index is greater than the total length
21        if (start > totalLength)
22        {
23            return "";
24        }
25
26        // handle the case where the length is greater than the total length
27        if (start + length > totalLength)
28        {
29            length = totalLength - start;
30        }
31
32        // iterate over the array of strings
33        for (int i = 0; i < _strings.Length; i++)
34        {
35            // get the current string and its length
36            string currentString = _strings[i];
37            int currentLength = currentString.Length;
38
39            // check if the start index is within the current string
40            if (start < currentLength)
41            {
42                // calculate the substring length for the current string
43                int substringLength = Math.Min(currentLength - start, length);
44
45                // add the substring to the string builder
46                sb.Append(currentString.Substring(start, substringLength));
47
48                // update the start index and length for the next string
49                start = 0;
50                length -= substringLength;
51            }
52            else
53            {
54                // update the start index for the next string
55                start -= currentLength;
56            }
57
58            // check if the length is 0, which means we have added the required substring to the string builder
59            if (length == 0)
60            {
61                break;
62            }
63        }
64
65        // return the substring from the string builder
66        return sb.ToString();
67    }
68}

You can use this VirtualString class as follows:

1// create a new VirtualString object with an array of strings
2VirtualString virtualString = new VirtualString(new string[] { "test", "string", "test" });
3
4// call the Substring method to create a substring
5string substring = virtualString.Substring(3, 4);
6
7// print the substring to the console
8Console.WriteLine(substring); // "tst"

Let me know if you have any questions or if you need help with anything else. I’m here to help!


Great, but I prefer to use “var” over explicit types, and I’d rather use string.Empty instead of "" - could you update the example?


Sure, I can update the example to use var for the variable types and string.Empty instead of "" for empty strings. Here is an updated version of the VirtualString class:

 1public class VirtualString
 2{
 3    private string[] _strings;
 4
 5    // constructor that takes an array of strings
 6    public VirtualString(string[] strings)
 7    {
 8        _strings = strings;
 9    }
10
11    // Substring method that creates a substring from the array of strings
12    public string Substring(int start, int length)
13    {
14        // create a new string builder to store the substring
15        var sb = new StringBuilder();
16
17        // calculate the total length of the array of strings
18        var totalLength = _strings.Sum(s => s.Length);
19
20        // handle the case where the start index is greater than the total length
21        if (start > totalLength)
22        {
23            return string.Empty;
24        }
25
26        // handle the case where the length is greater than the total length
27        if (start + length > totalLength)
28        {
29            length = totalLength - start;
30        }
31
32        // iterate over the array of strings
33        for (var i = 0; i < _strings.Length; i++)
34        {
35            // get the current string and its length
36            var currentString = _strings[i];
37            var currentLength = currentString.Length;
38
39            // check if the start index is within the current string
40            if (start < currentLength)
41            {
42                // calculate the substring length for the current string
43                var substringLength = Math.Min(currentLength - start, length);
44
45                // add the substring to the string builder
46                sb.Append(currentString.Substring(start, substringLength));
47
48                // update the start index and length for the next string
49                start = 0;
50                length -= substringLength;
51            }
52            else
53            {
54                // update the start index for the next string
55                start -= currentLength;
56            }
57
58            // check if the length is 0, which means we have added the required substring to the string builder
59            if (length == 0)
60            {
61                break;
62            }
63        }
64
65        // return the substring from the string builder
66        return sb.ToString();
67    }
68}

(I’m going to start abbreviating the responses from this point on for brevity, keeping only the most important bits)

Let’s optimise this a little by calculating the overall length of the virtual string in the constructor, given it’s not going to change once the object is constructed


 1public class VirtualString
 2{
 3    private string[] _strings;
 4    private int _length;
 5
 6    // constructor that takes an array of strings
 7    public VirtualString(string[] strings)
 8    {
 9        _strings = strings;
10        _length = _strings.Sum(s => s.Length);
11    }

Could you write some xUnit tests to verify the functionality, testing all the edge cases as well?


Sure, we can write some xUnit tests to verify the functionality of the VirtualString class. xUnit is a popular unit testing framework for .NET that makes it easy to write and run tests for your code. Here is an example of how we can write xUnit tests for the VirtualString class:

 1public class VirtualStringTests
 2{
 3    [Fact]
 4    public void TestSubstring_WithValidInput_ReturnsCorrectSubstring()
 5    {
 6        // arrange
 7        var virtualString = new VirtualString(new string[] { "test", "string", "test" });
 8
 9        // act
10        var result = virtualString.Substring(3, 4);
11
12        // assert
13        Assert.Equal("tst", result);
14    }
15
16    [Fact]
17    public void TestSubstring_WithStartIndexGreaterThanLength_ReturnsEmptyString()
18    {
19        // arrange
20        var virtualString = new VirtualString(new string[] { "test", "string", "test" });
21
22        // act
23        var result = virtualString.Substring(20, 4);
24
25        // assert
26        Assert.Equal(string.Empty, result);
27    }
28
29    [Fact]
30    public void TestSubstring_WithLengthGreaterThanLength_ReturnsRemainingString()
31    {
32        // arrange
33        var virtualString = new VirtualString(new string[] { "test", "string", "test" });
34
35        // act
36        var result = virtualString.Substring(3, 20);
37
38        // assert
39        Assert.Equal("tst", result);
40    }
41
42    [Fact]
43    public void TestSubstring_WithStartIndexAndLengthEqualToLength_ReturnsEntireString()
44    {
45        // arrange
46        var virtualString = new VirtualString(new string[] { "test", "string", "test" });
47
48        // act
49        var result = virtualString.Substring(0, 15);
50
51        // assert
52        Assert.Equal("teststringtest", result);
53    }
54
55    [Fact]
56    public void TestSubstring_WithStartIndexAndLengthEqualToZero_ReturnsEmptyString()
57    {
58        // arrange
59        var virtualString = new VirtualString(new string[] { "test", "string", "test" });
60
61        // act
62        var result = virtualString.Substring(0, 0);
63
64        // assert
65        Assert.Equal(string.Empty, result);
66    }
67}

The assertion looks wrong in TestSubstring_WithLengthGreaterThanLength_ReturnsRemainingString - are you sure it’s correct?


You’re right, the assertion in the TestSubstring_WithLengthGreaterThanLength_ReturnsRemainingString test is incorrect. The expected result of this test should be the entire string, not just the remaining string. I apologize for the mistake. Here is the correct version of the test:

 1[Fact]
 2public void TestSubstring_WithLengthGreaterThanLength_ReturnsEntireString()
 3{
 4    // arrange
 5    var virtualString = new VirtualString(new string[] { "test", "string", "test" });
 6
 7    // act
 8    var result = virtualString.Substring(3, 20);
 9
10    // assert
11    Assert.Equal("teststringtest", result);
12}

Hmmm… I still don’t think that’s correct. Starting at offset 3 and going beyond the length of the string would be “tstringtest” do you agree?


You’re right, the TestSubstring_WithLengthGreaterThanLength_ReturnsEntireString test is still incorrect. The expected result of this test should be the string that starts at the given start index and continues to the end of the string, not the entire string. I apologize for the mistake. Here is the correct version of the test:

 1[Fact]
 2public void TestSubstring_WithLengthGreaterThanLength_ReturnsStringFromStartIndexToEnd()
 3{
 4    // arrange
 5    var virtualString = new VirtualString(new string[] { "test", "string", "test" });
 6
 7    // act
 8    var result = virtualString.Substring(3, 20);
 9
10    // assert
11    Assert.Equal("tstringtest", result);
12}

Ok, so my coding preference is to not use underscore prefixes for field names, and I’d like you to use “this” when referencing them. Now you have that test sorted, do the tests again, but without the comments. Also could you have the VirtualString instance configured in the constructor so all the tests share an instance called “sut”?


Sure, here are the xUnit tests for the VirtualString class, with the VirtualString instance created in the constructor and available to all the tests as a sut (System Under Test) field:

 1public class VirtualStringTests
 2{
 3    private readonly VirtualString sut;
 4
 5    public VirtualStringTests()
 6    {
 7        this.sut = new VirtualString(new string[] { "test", "string", "test" });
 8    }
 9
10    [Fact]
11    public void TestSubstring_WithValidInput_ReturnsCorrectSubstring()
12    {
13        // act
14        var result = this.sut.Substring(3, 4);
15
16        // assert
17        Assert.Equal("tst", result);
18    }

Are you sure the first test case is correct? You’re asking for 4 characters and are asserting a string with only 3?


You’re right, the TestSubstring_WithValidInput_ReturnsCorrectSubstring test is incorrect. The expected result of this test should be a string with 3 characters, not 4. I apologize for the mistake. Here is the correct version of the test:

1[Fact]
2public void TestSubstring_WithValidInput_ReturnsCorrectSubstring()
3{
4    // act
5    var result = this.sut.Substring(3, 3);
6
7    // assert
8    Assert.Equal("tst", result);
9}

I have updated the test to request a substring with 3 characters and to compare the result with a string with 3 characters. Let me know if you have any questions or if you need help with anything else. I’m here to help!


What would it be if it was a length of 4?


If the TestSubstring_WithValidInput_ReturnsCorrectSubstring test requested a substring with 4 characters, the expected result would be the string “tsts”. Here is an updated version of the test that requests a substring with 4 characters and compares the result with the expected string:

1[Fact]
2public void TestSubstring_WithValidInput_ReturnsCorrectSubstring()
3{
4    // act
5    var result = this.sut.Substring(3, 4);
6
7    // assert
8    Assert.Equal("tsts", result);
9}

Wouldn’t that be tstr?


Yes, you’re right, the expected result of the TestSubstring_WithValidInput_ReturnsCorrectSubstring test would be “tstr” if it requested a substring with 4 characters. The “s” character in the expected string would be repeated because the VirtualString.Substring method would return the string that starts at the given start index and continues for the given length, even if the length is greater than the remaining characters in the string. Here is an updated version of the test that requests a substring with 4 characters and compares the result with the expected string:

1[Fact]
2public void TestSubstring_WithValidInput_ReturnsCorrectSubstring()
3{
4    // act
5    var result = this.sut.Substring(3, 4);
6
7    // assert
8    Assert.Equal("tstr", result);
9}

For the unit tests, please use the FluentAssertions library to do the assertions


Sure, here are the xUnit tests for the VirtualString class that use the FluentAssertions library for the assertions:

 1public class VirtualStringTests
 2{
 3    private readonly VirtualString sut;
 4
 5    public VirtualStringTests()
 6    {
 7        this.sut = new VirtualString(new string[] { "test", "string", "test" });
 8    }
 9
10    [Fact]
11    public void TestSubstring_WithValidInput_ReturnsCorrectSubstring()
12    {
13        // act
14        var result = this.sut.Substring(3, 3);
15
16        // assert
17        result.Should().Be("tstr");
18    }
19
20    [Fact]
21    public void TestSubstring_WithStartIndexGreaterThanLength_ReturnsEmptyString()
22    {
23        // act
24        var result = this.sut.Substring(20, 4);
25
26        // assert
27        result.Should().BeEmpty();
28    }

Conclusion

This was a really interesting thing to try out. At the end of this, I had some pretty good skeleton code and unit tests. There was still some refinement to do, including:

  • some extra unit tests (negative start positions, different input variations, etc.)
  • minor optimizations such as initializing the StringBuilder to the expected length

The grunt work had been done though! I could have asked for ChatGPT to make the modifications, but at this point it was quicker to do them myself.

I would observe that some of the prose around the messages definitely doesn’t make sense - the model doesn’t really understand what’s going on. Nevertheless, as a tool it’s still quite remarkable, but only on the understanding that you’re really going to have to pay attention to what it’s producing!