Let's look at: Trace, Profiling, Integration Testing, Code Coverage, Mocking, Deadlock Detection in Go vs Java | Testing, Debugging and Profiling

Series: Go for Java Developers — analysis of trace, profiling and testing

In this article we will analyze tools and practices for testing, debugging and profiling in Go. For a Java developer this will make it easy to navigate in Go, and for a Go developer — to compare approaches with Java.


Trace — program execution tracking

Go has a built-in package runtime/trace for detailed program analysis. Allows tracking goroutine, syscalls and execution time.

Trace example

// Go
import (
    "os"
    "runtime/trace"
    "time"
)

func main() {
    f, _ := os.Create("trace.out")
    defer f.Close()
    trace.Start(f)
    defer trace.Stop()

    time.Sleep(time.Second) // example work
}

Java equivalent: via Flight Recorder or JVisualVM profiler

// Java (example of recording events via Flight Recorder API)
import jdk.jfr.Recording;

public class Main {
    public static void main(String[] args) throws Exception {
        try (Recording recording = new Recording()) {
            recording.start();
            Thread.sleep(1000); // example work
            recording.stop();
            recording.dump(new java.io.File("trace.jfr").toPath());
        }
    }
}
Trace is useful for deep performance analysis and identifying bottlenecks, especially with concurrency.

CPU and memory profiling

Go provides the pprof package for CPU and memory profiling. This helps optimize resource usage.

CPU profile example

// Go
import (
    "os"
    "runtime/pprof"
)

func main() {
    f, _ := os.Create("cpu.prof")
    defer f.Close()
    pprof.StartCPUProfile(f)
    defer pprof.StopCPUProfile()

    // load
    for i := 0; i < 1000000; i++ {}
}

Java equivalent: JVisualVM profiler or Java Flight Recorder

// Java
// CPU and heap profiling via JVisualVM / Flight Recorder
// Example of profiling manually in Java is not needed, GUI is used
Tip: profile CPU and memory regularly, especially under high loads and multithreading.

Integration Testing — integration tests

Go uses the testing package for unit and integration tests. Java uses JUnit/TestNG. In Go, tests are written simply and composed via *_test.go files.

Integration test example

// Go
import "testing"

func TestIntegration(t *testing.T) {
    result := 1 + 2
    if result != 3 {
        t.Error("Expected 3")
    }
}

Java equivalent:

// Java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class MainTest {
    @Test
    void testIntegration() {
        assertEquals(3, 1 + 2);
    }
}

Code Coverage — test coverage

Go supports code coverage analysis via go test -cover. Java — via JaCoCo or built-in IDE tools.

Command example

// Go
go test -cover ./...
Tip: 80%+ coverage is important for stability and confidence in code changes.

Mocking interfaces — dependency substitution

Go allows mocking interfaces manually or via third-party libraries (gomock). Java uses Mockito or PowerMock.

Go example

// Go
type DB interface {
    Get(id int) string
}

type MockDB struct{}

func (m MockDB) Get(id int) string {
    return "mocked"
}

Java equivalent:

// Java
import static org.mockito.Mockito.*;

interface DB {
    String get(int id);
}

public class Test {
    public void test() {
        DB mockDb = mock(DB.class);
        when(mockDb.get(1)).thenReturn("mocked");
    }
}

Deadlock detection — detecting deadlocks

Go race detector (go run -race) helps identify races and deadlocks. Java — thread dump, VisualVM or built-in IDE tools.

// Go
go run -race main.go
Tip: races and deadlocks are major multithreading issues. Use built-in tools for quick detection.

Comparison table Go ↔ Java

Concept Go Java Comment
Trace runtime/trace Flight Recorder / JVisualVM Detailed execution analysis
CPU / Memory profiling pprof JVisualVM / Flight Recorder Resource usage analysis
Integration testing testing package, *_test.go JUnit / TestNG Simple test writing
Code coverage go test -cover JaCoCo / IDE Tracking coverage
Mocking interfaces manual or gomock Mockito / PowerMock Dependency substitution for tests
Deadlock detection race detector thread dump / VisualVM Identifying races and locks

Practical conclusion

  • Go has built-in tools for testing, profiling and debugging — easier to integrate into pipeline.
  • Java offers powerful third-party tools and IDE, but requires more setup.
  • Race detector and pprof — key tools for safe concurrency work.
  • Mocking and coverage are convenient for maintainable and reliable code.
  • Comparing approaches Go ↔ Java allows quick adaptation between languages and using best practices.

🌐 На русском
Total Likes:0

Оставить комментарий

My social media channel
By sending an email, you agree to the terms of the privacy policy

Useful Articles:

Atomic vs Mutex, Blocking vs Non-Blocking, Read/Write Splitting (RWMutex), Logging | Concurrency Patterns and Best Practices part 5 | Go ↔ Java
In this article, we will analyze the key approaches to working with parallelism and synchronization in Go and Java. We will compare how the same tasks are solved in these languages, show idioms, patte...
Context, propagation, and cancellation patterns in Go vs Java | Patterns, idioms, and best practices of Go
← Related articles: Part 1 — Error handling and defer in Go (Concurrency and synchronization) | Patterns, idioms and best practices for Go 1. Context and its role In Go context.Context is used to...
Scheduler internals in Go ↔ Java: how your code is actually executed
When you write go func() or create a Thread in Java, it seems like you are managing concurrency. But in reality, you are passing the task to the scheduler. And this is where the real show begins. Go...

New Articles:

Concurrency is not about “starting many threads”. It’s about agreements between them. Imagine a restaurant kitchen: — cooks (threads / goroutines) — orders (tasks) — and the main question: how do th...
When HashMap starts killing production: the engineering story of ConcurrentHashMap
Imagine a typical production service. 32 CPU hundreds of threads configuration / session / rate limits cache tens of thousands of operations per second And somewhere inside — a regular Map. At first...
Zero Allocation in Java: what it is and why it matters
Zero Allocation — is an approach to writing code in which no unnecessary objects are created in heap memory during runtime. The main idea: fewer objects → less GC → higher stability and performance. ...
Fullscreen image