XCTestGen: CLI for automatic XCTest generation using ChatGPT
Tags: swift, chatgpt, openai, cli
Published: 2023-03-14 13:52:24

# Introduction
As the title suggests, I created a tool for automatic generation of Swift's XCTest using ChatGPT, which is very popular nowadays. I will briefly explain how I developed it and how to use it.
I would be happy if you starred it.

{% embed https://github.com/NakaokaRei/XCTestGen %}

# Creation of CLI tools
Using Swift Packages, we have created a CLI tool. The template is created by executing the following command under the working directory

```
$ swift package init --type executable
```

# Automatic generation of XCTest
As the title suggests, ChatGPT is used for automatic generation of XCTests, and the following commands are given to ChatGPT to write XCTest.

```
Please write a unit test (XCTest) of the following Swift code. No explanatory text is required as we would like to save your output as source code as is.

// Sample.swift
class Sample {
    func sample(input: String) -> String {
    // 任意の処理
    }
}
```

The following libraries were used to utilize OpenAI's API in Swift.

https://github.com/adamrushy/OpenAISwift

Throw the above instruction to ChatGPT and save the response in the specified folder. If you want to know more details, please see the [code](https://github.com/NakaokaRei/XCTestGen/blob/c11ff20bf5a44f5073ec06967325367b663a5afd/Sources/XCTestGen/XCTestGen.swift#L33) in the repository.

```swift
func generateXCTest() async throws {
        let order = "Please write a unit test (XCTest) of the following Swift code. No explanatory text is required as we would like to save your output as source code as is."
        let code: String

        do {
            code = try FileManager.readTextFile(atPath: input)
        } catch {
            throw XCTestGenError.readCodeError
        }

        let prompt = order + "\n\n" + code

        let openAI = OpenAISwift(authToken: key)
        let result = try await openAI.sendCompletion(with: prompt, maxTokens: maxTokens)

        guard let testCode = result.choices.first?.text else {
            throw XCTestGenError.chatgptResultError
        }

        do {
            try FileManager.saveTextFile(text: testCode, atPath: outputTestsPath())
        } catch {
            throw XCTestGenError.saveFileError
        }
    }
```

# Usage

## Installation
Execute the following command

```
$ git clone git@github.com:NakaokaRei/XCTestGen.git
$ cd XCTestGen
$ swift build -c release
$ cp .build/release/XCTestGen /usr/local/bin/XCTestGen
```

## Generate XCTest
Execute the following command. In order from the first argument, "API Key of OpenAI", "Path of the Swift file for which you want to automatically generate tests", and "Directory of the output destination".

```
$ XCTestGen $OPENAI_KEY "input file path" "output dir path"
```

If you give the following code

```swift
// Sample.swift
class Sample {
    func sample1(x: Int) -> Int {
        return x + 1
    }

    func sample2(x: String) -> String {
        return x + "sample"
    }
}
```

The following code is generated and saved

```swift
// SampleTests.swift
import XCTest

class SampleTests: XCTestCase {

    func test_sample1() {
        // given
        let expectedResult = 11
        let sampleInput = 10
        let sut = Sample()

        // when
        let result = sut.sample1(x: sampleInput)

        // then
        XCTAssertEqual(result, expectedResult)
    }

    func test_sample2() {
        // given
        let expectedResult = "testsample"
        let sampleInput = "test"
        let sut = Sample()

        // when
        let result = sut.sample2(x: sampleInput)

        // then
        XCTAssertEqual(result, expectedResult)
    }
}
```

# Conclusion
The current issue is that the test code generated from ChatGPT is not checked for grammatical correctness, so I think it would be better to use SwiftLint or other tools to perform at least some checks.
In the future, we plan to add a plugin function to Swift Package Manger so that it can be easily introduced into Xcode projects.

I'm waiting for Issue and PR!!!!