GO
TS

Go to TypeScript

10 lessons

Progress0%
1Variables & Types2Functions3Structs & Classes4Interfaces — Both Use Structural Typing5Error Handling6Goroutines vs Async/Await7Slices & Maps → Arrays & Objects8Packages & Modules9Testing10Generics
All Mirror Courses
GO
TS
Testing
MirrorLesson 9 of 10
Lesson 9

Testing

Go testing package vs Jest/Vitest — table-driven vs describe/it

Introduction

In this lesson, you'll learn about testing in TypeScript. Coming from Go, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
GO
From Go:

In Go, you're familiar with go testing package vs jest/vitest — table-driven vs describe/it.

TS
In TypeScript:

TypeScript has its own approach to go testing package vs jest/vitest — table-driven vs describe/it, which we'll explore step by step.

The TypeScript Way

Let's see how TypeScript handles this concept. Here's a typical example:

TS
TypeScript Example
// math.test.ts
import { describe, it, expect, test } from "vitest";
import { add, divide } from "./math";

describe("math", () => {
  it("adds two numbers", () => {
    expect(add(1, 2)).toBe(3);
  });

  // Table-driven via test.each
  test.each([
    [6, 3, 2],
    // [1, 0, throws]
  ])("divide(%i,%i)=%i", (a, b, expected) => {
    expect(divide(a, b)).toBe(expected);
  });

  it("throws on divide by zero", () => {
    expect(() => divide(1, 0)).toThrow("division by zero");
  });
});

// npx vitest         | watch mode
// npx vitest run     | single run
// npx vitest --coverage

Comparing to Go

Here's how you might have written similar code in Go:

GO
Go (What you know)
// math_test.go
package math

import (
    "fmt"
    "testing"
)

func TestAdd(t *testing.T) {
    if got := Add(1, 2); got != 3 {
        t.Errorf("Add(1,2)=%d; want 3", got)
    }
}

// Table-driven tests
func TestDivide(t *testing.T) {
    tests := []struct {
        a, b    float64
        want    float64
        wantErr bool
    }{
        {6, 3, 2, false},
        {1, 0, 0, true},
    }
    for _, tc := range tests {
        t.Run(fmt.Sprintf("%v/%v", tc.a, tc.b), func(t *testing.T) {
            got, err := Divide(tc.a, tc.b)
            if tc.wantErr {
                if err == nil { t.Error("expected error") }
                return
            }
            if err != nil { t.Fatalf("unexpected: %v", err) }
            if got != tc.want { t.Errorf("got %v; want %v", got, tc.want) }
        })
    }
}

func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ { Add(1, 2) }
}
// go test ./... | go test -v -run TestAdd | go test -bench=.
Mirror Card
GO
From Go:

You may be used to different syntax or behavior.

TS
In TypeScript:

Go: _test.go files, TestXxx(t *testing.T), no framework needed

Mirror Card
GO
From Go:

You may be used to different syntax or behavior.

TS
In TypeScript:

TypeScript: .test.ts files, describe/it blocks, Vitest/Jest framework

Mirror Card
GO
From Go:

You may be used to different syntax or behavior.

TS
In TypeScript:

Table-driven (Go) vs test.each (Vitest) for parametrized tests

Mirror Card
GO
From Go:

You may be used to different syntax or behavior.

TS
In TypeScript:

Go error test: check err != nil; TypeScript: expect().toThrow()

Mirror Card
GO
From Go:

You may be used to different syntax or behavior.

TS
In TypeScript:

Go benchmarks built-in; TypeScript needs separate benchmark library

Step-by-Step Breakdown

1. Test Structure

Go functions are TestXxx(t *testing.T) in _test.go files — no framework. TypeScript uses describe/it blocks with a framework.

GO
Go
describe("add", () => {
  it("returns sum", () => {
    expect(add(1,2)).toBe(3);
  });
});
TS
TypeScript
func TestAdd(t *testing.T) {
    if got := Add(1,2); got != 3 {
        t.Errorf("got %d; want 3", got)
    }
}

2. Parametrized Tests

Go uses table-driven tests (struct slice + t.Run). TypeScript uses test.each. Both create sub-tests for each case.

GO
Go
test.each([[1,2,3],[4,5,9]])("adds", ([a,b,c]) =>
  expect(add(a,b)).toBe(c));
TS
TypeScript
tests:=[]struct{a,b,want int}{{1,2,3},{4,5,9}}
for _,tc:=range tests {
  t.Run(fmt.Sprintf("%d+%d",tc.a,tc.b),func(t *testing.T){
    if Add(tc.a,tc.b)!=tc.want{t.Fail()}
  })
}

3. Testing Errors

Go returns errors as values — check err != nil. TypeScript throws exceptions — use expect().toThrow().

GO
Go
expect(() => divide(1, 0)).toThrow("division by zero");
TS
TypeScript
_, err := Divide(1, 0)
if err == nil {
    t.Error("expected error for divide by zero")
}

4. Benchmarks

Go benchmarks are BenchmarkXxx(b *testing.B) — built in. TypeScript needs benchmark.js or vitest-bench for equivalent functionality.

GO
Go
// vitest-bench or benchmark.js
bench("add", () => { add(1, 2); });
TS
TypeScript
func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ { Add(1, 2) }
}
// go test -bench=BenchmarkAdd -benchmem

Common Mistakes

When coming from Go, developers often make these mistakes:

  • Go: _test.go files, TestXxx(t *testing.T), no framework needed
  • TypeScript: .test.ts files, describe/it blocks, Vitest/Jest framework
  • Table-driven (Go) vs test.each (Vitest) for parametrized tests
Common Pitfall
Don't assume TypeScript works exactly like Go. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • Go: _test.go + TestXxx(t *testing.T) — no framework; TypeScript: .test.ts + describe/it
  • Table-driven (Go) vs test.each (TypeScript) for parametrized tests
  • Go tests errors with err != nil; TypeScript uses expect().toThrow()
  • Go benchmarks are built-in; TypeScript needs external benchmark libraries
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your Go code in TypeScript to practice these concepts.
PreviousNext