Software-Testing mit ChatGPT als Assistent

Sprachmodelle können manuelle Tests zwar nicht ersetzen, aber eine sinnvolle und effiziente Ergänzung sein.

In Pocket speichern vorlesen Druckansicht 14 Kommentare lesen
Chatgpt,Chat,With,Ai,Or,Artificial,Intelligence.,Young,Businessman,Chatting

(Bild: CHUAN CHUAN/Shutterstock.com)

Lesezeit: 10 Min.
Von
  • Nicolas Langemann
Inhaltsverzeichnis

Large Language Models (LLM) können die Effizienz in Softwareprojekten verbessern. ChatGPT hilft nicht nur beim Schreiben von Code, sondern auch beim Erstellen von Softwaretests. Das funktioniert besonders gut, wenn Entwicklungsteams automatisierte Tests benötigen und es wenig fundamentale Abweichung in den Testfällen gibt. Der folgende Text stellt ein Fallbeispiel anhand von Ende-zu-Ende-Tests (E2E) mit dem Testframework Cypress vor.

Softwaretests tragen dazu bei, die Funktionsweise und Zuverlässigkeit einer Anwendung zu gewährleisten. Sie sind ein essenzieller Bestandteil jedes Softwareentwicklungsprojekts. Das Erstellen der Tests ist allerdings potenziell zeitaufwendig und wird daher oft vernachlässigt. Teams müssen nicht nur die Software kontinuierlich warten, sondern die Tests parallel zum Entwicklungsprozess erstellen und aktualisieren. Das erfordert ständige Aufmerksamkeit und damit Zeit und Aufwand auf Entwicklerseite.

Diese Herausforderungen beeinflussen anders als standardisierte Tests ständig das Abwägen von Kosten und Nutzen für die Weiterentwicklung der Anwendung. Kippt das Verhältnis, leidet die Softwarequalität. Es gibt daher immer wieder Projekte, für die es günstiger ist, eine Armada von Testern an das Projekt zu setzen, statt automatisierte Tests zu entwickeln und zu aktualisieren. Je schneller sich das Produkt ändert, desto gravierender wird die Problematik.

Multiplattformfähige Softwareprojekte stellen die beteiligten Entwickler vor zusätzliche Herausforderungen. Gegebenenfalls muss die gleiche Funktion in allen unterstützten Umgebungen getestet werden. Somit multipliziert sich der Aktualisierungsaufwand der Tests mit der Anzahl der Zielplattformen.

Wer beispielsweise an einem Angular-Projekt mit Capacitor arbeitet, das eine Webanwendung in eine native Anwendung verpackt, kann E2E-Tests wahlweise in Cypress, Espresso oder XCTest schreiben. ChatGPT kann beim Schreiben von standardisierten Tests unterstützen und damit Kapazitäten für anspruchsvollere Aufgaben freimachen.

Als leistungsfähiger Textgenerator kann das KI-System Testcodes basierend auf vorhandenen Testfällen oder Anforderungen generieren. Teams können damit

  • Zeit und Ressourcen sparen: Statt manuell separate Testcodes fĂĽr Web, Android und iOS zu schreiben, hilft ChatGPT dabei, Testcode auf der Grundlage vordefinierter Testfälle automatisch zu generieren.
  • Konsistenz gewährleisten: Die von dem KI-Modell erstellten Testcodes sind konsistent und decken den gleichen Testfall ab.
  • Wartungsaufwand reduzieren: Da ChatGPT den Testcode automatisch generiert, ist es einfacher, die Testfälle zu aktualisieren und die Testcodes auf mehreren Plattformen zu synchronisieren.

Grundsätzlich lassen sich auch andere LLMs nutzen, aber dieser Artikel setzt beispielhaft auf ChatGPT-4 von OpenAI, weil es sich einfach integrieren lässt und gute Performance bietet. Es gibt auch Fälle, in denen der Einsatz von ChatGPT zum Generieren von Tests nur geringen Nutzen bringt. Es gibt jedoch diverse Szenarien, in denen LLMs helfen können, die Ressourcen in Projekten effizienter einzusetzen:

  1. Das Projekt benötigt viele Testfälle, die schnell und effizient erstellt werden sollen. Hierbei bauen die Testfälle auf ähnlichen Mustern auf, die sich mit geringer Variation wiederholen.
  2. Ein Team will den Wartungsaufwand fĂĽr den Testcode reduzieren und dessen Erstellung vom Entwickeln der eigentlichen Anwendung entkoppeln.
  3. Eine Projektanwendung läuft auf mehreren Plattformen und für alle Testfälle sollen eigene Tests für jede Plattform laufen.
  4. Die fĂĽr das Projekt verfĂĽgbaren Ressourcen sind begrenzt. Daher muss das Team den Testcode schnell und effizient erstellen, um Kosten und Zeit zu sparen.

ChatGPT eignet sich besonders gut für Projekte, die viele automatisierte Tests benötigen und bei denen es wenig Abweichung in den Testabläufen gibt. Auf die Weise können Teams das Schreiben von Testcode von der Entwicklung der Anwendung trennen und damit nach dem DRY-Prinzip (Don't Repeat Yourself) arbeiten.

Noch wichtiger ist, dass der Einsatz von ChatGPT Kapazitäten für die komplexen Herausforderungen freischaufelt und die spezifischen Probleme eines Projekts mehr Aufmerksamkeit erhalten. Für das Projektmanagement entsteht kein zusätzlicher finanzieller Druck, da es bestehende Ressourcen umverteilt und zunächst keine zusätzlichen Ressourcen schaffen muss.

Folgendes Beispiel dreht sich um den Test eines einfachen Angular-Services, der über die öffentliche JSONPlaceholder-API Nutzerdaten abruft.

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = 
    'https://jsonplaceholder.typicode.com/users';

  constructor(private http: HttpClient) { }

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>(this.apiUrl);
  }

  getUserById(id: number): Observable<User> {
    return this.http.get<User>(`${this.apiUrl}/${id}`);
  }
}

Das Objekt UserService stellt zwei Methoden bereit: getUsers() ruft alle Nutzer ab und getUserById(id: number) gibt einen User anhand seiner ID zurĂĽck.

Die Angular-Komponente verwendet den Service folgendermaĂźen:

import { UserService, User } from './user.service';

@Component({
  ...
})
export class AppComponent implements OnInit {
  users: User[] = [];

  constructor(private userService: UserService) {}

  ngOnInit() {
    this.userService.getUsers().subscribe(users => {
      this.users = users;
    });
  }
}

Diese Komponente nutzt die Methode UserService, um alle Nutzerdaten abzurufen und im users-Array zu speichern. AnschlieĂźend werden die Nutzerdaten in der Komponentenvorlage angezeigt:

<ul>
  <li *ngFor="let user of users">
    {{ user.name }} ({{ user.email }})
  </li>
</ul>

ChatGPT kann für diese Anwendung unterschiedliche Standardtests schreiben. Statt jedoch einfach den vollständig zu testenden Code an ChatGPT zu übergeben, empfiehlt es sich eine Testfalldefinition zu erstellen. Auf diese Weise können Teams die Tests in einer externen Datei speichern, anpassen und erweitern, ohne den ChatGPT-Prompt ändern zu müssen. Das senkt die Kosten und reduziert zusätzlich das Risiko von Datendiebstahl, da nur die Informationen an die ChatGPT-API übertragen werden, die zum Generieren der Tests erforderlich sind.

Der erste Schritt ist, eine JSON-Datei user-service-tests.json zu erstellen, um die Testfälle für den Angular-Service zu definieren. Sie sollte sowohl die Informationen zu den Methoden des zu testenden Dienstes enthalten als auch eine Beschreibung der erwarteten Ergebnisse.

{
  "unitTests": [
    {
      "description": "getUserById",
      "method": "getUserById",
      "input": 1,
      "output": {
        "id": 1,
        "name": "John Doe",
        "email": "john.doe@example.com"
      }
    },
    {
      "description": "getUsers",
      "method": "getUsers",
      "output": [
        {
          "id": 1,
          "name": "John Doe",
          "email": "john.doe@example.com"
        },
        {
          "id": 2,
          "name": "Jane Doe",
          "email": "jane.doe@example.com"
        }
      ]
    }
  ],
  "e2eTests": [
    {
      "description": "Display users list",
      "endpoint": "/users",
      "expectedElements": [
        "#user-1",
        "#user-2"
      ]
    }
  ]
}

Zur Interaktion mit ChatGPT dient die OpenAI-API. Voraussetzung dafĂĽr ist ein API-Key, fĂĽr den ein kostenpflichtiger OpenAI-Account erforderlich ist.

FĂĽr die Kommunikation mit OpenAI kommt der Http-Client Axios zum Einsatz, der sich mit

npm install axios​

installieren lässt.

Der folgende Code zeigt ein Wrapper-Skript mit dem Namen "your-chatgpt-wrapper". Das Skript muss den zuvor generierten API-Key und die dazugehörige API-URL enthalten. Es empfiehlt sich, mindestens den Wert für den API-Key aus einem sicheren Secret Store zu laden, statt ihn im Skript fest zu kodieren.

import axios from 'axios';

const API_KEY = 'your_openai_api_key';
const API_URL = 
  'https://api.openai.com/v1/chat/completions';

// Configure Axios instance
const axiosInstance = axios.create({
  baseURL: API_URL,
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${API_KEY}`,
  },
});

export async function chatGpt(prompt: string): 
  Promise<string> {
  try {
    const response = await axiosInstance.post('', {
      messages: [
        { role: "user", content: prompt}
      ],
      max_tokens: 100,
      n: 1,
      stop: null,
      temperature: 0.7,
    });

    const generatedCode = 
      response.data.choices[0].message.content.trim();
    return generatedCode || '';
  } catch (error) {
    console.error('Error while communicating with ' +
                  'ChatGPT API:', error);
    throw error;
  }
}

export async function generateJasmineTest(testCase: any): 
  Promise<string> {
  const prompt = `Generate a Jasmine unit test
    for the following test case:
    \n\n${JSON.stringify(testCase, null, 2)}\n\n
    Jasmine test:`;
  return chatGpt(prompt);
}

export async function 
  generateCypressTest(testCase: any): Promise<string> {
  const prompt = `Generate a Cypress E2E test for
    the following test case:
    \n\n${JSON.stringify(testCase, null, 2)}\n\n
    Cypress test:`;
  return chatGpt(prompt);
}

Der Code schickt die API-Anfragen mit Axios an den ChatGPT-API-Endpunkt. Daneben enthält er zwei weitere Funktionen: generateJasmineTest und generateCypressTest. Sie verwenden jeweils einen Testfall als Eingabe und geben den Testcode als Ausgabe zurück.

Eventuell sind einige Anpassungen erforderlich, um die ChatGPT-Antworten optimal zu nutzen, einschließlich der Auswahl unterschiedlicher Parameter für die API-Anfrage wie max_tokens, temperature oder stop. Die beste Vorgehensweise hängt von den spezifischen Anforderungen und dem gewünschten Testcode ab.