Repository: tmc/langchaingo
Pull request #28 (2023-04-19 10:01:34)

test: introduce stretchr/testify package for better test development experience
## Description

Improvements of #18 

- Replaced all test assert statement with `github.com/stretchr/testify/assert` and `github.com/stretchr/testify/require` packages for better DX and semantic

tmc: I'm a bit skeptical here -> I've always found the stdlib testing capabilities just fine. Is this really worth the dependency add? not many projects I work with use this library.
nekomeowww: > I'm a bit skeptical here -> I've always found the stdlib testing capabilities just fine. Is this really worth the dependency add? not many projects I work with use this library.

Answers:

 - Yes the capabilities is fine and suitable. You can use it, there is no blocking to the development of projects.
 - Since we will only need the `stretchr/testify` dependencies within the `*_test.go` files, there will be no affect on building artifacts, even though we implemented some utilities or mock helpers with `stretchr/testify` imported to help unit tests in the future, we can still state the build constraints in source code to prevent the tree-shaking not being worked properly.
 - I use `stretchr/testify` package all around my projects and also with my companies on work. It is stable and easy to use.

Benefits from `stretchr/testify`:

 - `stretchr/testify` is easy to use and understand with semantic statement.
   - When using `stretchr/testify/assert`, `assert` means assert the values, objects, identifiers with functions predefined.
   - When using `stretchr/testify/require`, `require` means assert with `t.FailNow()` feature included, it's like to say, "hey, if the assert is not success, then you fail, the rest of the test requires it".
   - When you read or review the code, unlike the current situation you must read with the possible nested `if...else...` statement, you simply understand where the assertion seeks or requires the statments to be `Equal` or `NotEqual`, `Nil` or `NotNil`, `NoError` or `ErrorIs` just by reading the function names.
 - `stretchr/testify` has a lot of underlying logicals to help assertion and predefined functions for assertion
   - It is not just comparing the values, it compares the types underneath it. Such as comparing `int8(10)` with `int64(10)` will produce assertion failure for developers to understand what is happened. Such as comparing the enums.
 - `stretchr/testify` provides detailed and nice output to tell you where the tests failed with the file names and line numbers printed out pretty for developers to understand.
   - Writing the assertion failure, error, fatal, log message for assertion result often copiable, and time wasting. With `stretchr/testify`, the messages will be generated for different functions, if you call `assert.Equal(...)`, once the assertion is failed, it would print out the expected and the actual values pretty without efforts.
    - The default `t.Log(..)` and `t.Fatal(...)` never print out the file names and line numbers unless you wrap a helper function to do so, this can be tricky to debug and find out where was wrong for developers if multiple assertion failure, error, fatal, log message were the same within the same testing function. And it is also hard to identify the assertion failure if you have unit tests failed in CI but passed in local development environment, where when `stretchr/testify` kicks in, the pressure and situation is way more clear and easy to deal with.
  - `stretchr/testify` provides more than just assert and log failures proprietary, it provides `mock` and `suite` for more structured testing for bigger projects.

Why:

  - I often find people write the tests incorrectly, such as the `t.Fatal(...)` or `t.FailNow()` would not be called at the situation where the testing should not continue, just like the situation you fixed in the previous PR. With `stretchr/testify`.
  - `if ... else ...` statement is tricky in unit tests. IMO, I think the assertion for `if ... else ...` is required to prevent some of the desired branch not tested, so when you need `if ... else ...` statements to not only help to assert, but also help on logical branching, it is easy to confuse developers and reduce the efficiency where it might lead to bugs and issues.
  - Just like what I said above, `stretchr/testify` can help developers and maintainers identify the failures easily.
  > it is also hard to identify the assertion failure if you have unit tests failed in CI but passed in local development environment
  - There is more...

Patch:
diff --git a/exp/document_loaders/doc.go b/exp/document_loaders/doc.go
index 4a8ab540..f0dcc8b1 100644
--- a/exp/document_loaders/doc.go
+++ b/exp/document_loaders/doc.go
@@ -1,3 +1,2 @@
 // Package document loaders includes an interface for loading and splitting documents from a source
-
 package document_loaders
diff --git a/go.mod b/go.mod
index 1b9c09d9..02f72610 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,12 @@ module github.com/tmc/langchaingo
 go 1.20
 
 require (
-	github.com/google/go-cmp v0.5.9
 	github.com/google/uuid v1.3.0
+	github.com/stretchr/testify v1.8.2
+)
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index c1f0f156..4fe79919 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,19 @@
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
+github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


Test patch:
diff --git a/exp/chains/llm_chain_test.go b/exp/chains/llm_chain_test.go
index 00f0ff2f..bb314c1f 100644
--- a/exp/chains/llm_chain_test.go
+++ b/exp/chains/llm_chain_test.go
@@ -5,41 +5,38 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"github.com/tmc/langchaingo/exp/prompts"
 	"github.com/tmc/langchaingo/llms/openai"
 )
 
 func TestLLMChain(t *testing.T) {
+	t.Parallel()
+
 	if openaiKey := os.Getenv("OPENAI_API_KEY"); openaiKey == "" {
 		t.Skip("OPENAI_API_KEY not set")
 	}
 	model, err := openai.New()
-	if err != nil {
-		t.Fatal(err)
-	}
+	require.NoError(t, err)
 
 	prompt, err := prompts.NewPromptTemplate("What is the capital of {country}", []string{"country"})
-	if err != nil {
-		t.Fatal(err)
-	}
+	require.NoError(t, err)
 
 	chain := NewLLMChain(model, prompt)
 
-	resultChainValue, err := Call(chain, map[string]any{"country": "France", "stop": []string{"\nObservation", "\n\tObservation"}})
-	if err != nil {
-		t.Fatal(err)
-	}
+	resultChainValue, err := Call(chain,
+		map[string]any{
+			"country": "France",
+			"stop":    []string{"\nObservation", "\n\tObservation"},
+		},
+	)
+	require.NoError(t, err)
 
 	resultAny, ok := resultChainValue["text"]
-	if !ok {
-		t.Error("No value in chain value text field")
-		return
-	}
+	require.True(t, ok)
 
 	result, _ := resultAny.(string)
 	result = strings.TrimSpace(result)
-
-	if result != "Paris." {
-		t.Errorf("Expected to get Paris. Got %s", result)
-	}
+	assert.Equal(t, "Paris", result)
 }
diff --git a/exp/chains/retrival_qa_chain_test.go b/exp/chains/retrival_qa_chain_test.go
index b2503cc4..05971e86 100644
--- a/exp/chains/retrival_qa_chain_test.go
+++ b/exp/chains/retrival_qa_chain_test.go
@@ -4,6 +4,7 @@ import (
 	"os"
 	"testing"
 
+	"github.com/stretchr/testify/require"
 	"github.com/tmc/langchaingo/llms/openai"
 	"github.com/tmc/langchaingo/schema"
 )
@@ -18,23 +19,20 @@ func (r testRetriever) GetRelevantDocuments(query string) ([]schema.Document, er
 }
 
 func TestRetrievalQAChain(t *testing.T) {
+	t.Parallel()
+
 	if openaiKey := os.Getenv("OPENAI_API_KEY"); openaiKey == "" {
 		t.Skip("OPENAI_API_KEY not set")
 	}
+
 	r := testRetriever{}
 	llm, err := openai.New()
-	if err != nil {
-		t.Errorf("Unexpected error %s", err.Error())
-		return
-	}
+	require.NoError(t, err)
 
 	chain := NewRetrievalQAChainFromLLM(llm, r)
 
 	_, err = Call(chain, map[string]any{
 		"query": "foz?",
 	})
-	if err != nil {
-		t.Errorf("Unexpected error %s", err.Error())
-		return
-	}
+	require.NoError(t, err)
 }
diff --git a/exp/chains/stuff_documents_chain_test.go b/exp/chains/stuff_documents_chain_test.go
index ab50eb08..873cc2eb 100644
--- a/exp/chains/stuff_documents_chain_test.go
+++ b/exp/chains/stuff_documents_chain_test.go
@@ -4,29 +4,26 @@ import (
 	"os"
 	"testing"
 
+	"github.com/stretchr/testify/require"
 	"github.com/tmc/langchaingo/exp/prompts"
 	"github.com/tmc/langchaingo/llms/openai"
 	"github.com/tmc/langchaingo/schema"
 )
 
 func TestStuffDocumentsChain(t *testing.T) {
+	t.Parallel()
+
 	if openaiKey := os.Getenv("OPENAI_API_KEY"); openaiKey == "" {
 		t.Skip("OPENAI_API_KEY not set")
 	}
 	model, err := openai.New()
-	if err != nil {
-		t.Errorf("Unexpected error %s", err)
-		return
-	}
+	require.NoError(t, err)
 
 	prompt, err := prompts.NewPromptTemplate(
 		"Print {context}",
 		[]string{"context"},
 	)
-	if err != nil {
-		t.Errorf("Unexpected error %s", err)
-		return
-	}
+	require.NoError(t, err)
 
 	llmChain := NewLLMChain(model, prompt)
 	chain := NewStuffDocumentsChain(llmChain)
@@ -41,8 +38,5 @@ func TestStuffDocumentsChain(t *testing.T) {
 	}
 
 	_, err = Call(chain, inputValues)
-	if err != nil {
-		t.Errorf("Unexpected error %s", err.Error())
-		return
-	}
+	require.NoError(t, err)
 }
diff --git a/exp/document_loaders/text_loader_test.go b/exp/document_loaders/text_loader_test.go
index ff1f5cd4..5357c733 100644
--- a/exp/document_loaders/text_loader_test.go
+++ b/exp/document_loaders/text_loader_test.go
@@ -3,31 +3,25 @@ package document_loaders
 import (
 	"testing"
 
-	"github.com/google/go-cmp/cmp"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestTextLoader(t *testing.T) {
+	t.Parallel()
+
 	loader := NewTextLoaderFromFile("./testdata/test.txt")
 
 	docs, err := loader.Load()
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if len(docs) != 1 {
-		t.Errorf("Number of docs from text loader expected to be 1")
-	}
+	require.NoError(t, err)
+	require.Len(t, docs, 1)
 
 	expectedPageContent := "Foo Bar Baz"
-	if docs[0].PageContent != expectedPageContent {
-		t.Errorf("Page content form text loader not the same as expected. Got:\n %s\nExpect:\n%s", docs[0].PageContent, expectedPageContent)
-	}
+	assert.Equal(t, docs[0].PageContent, expectedPageContent)
 
 	expectedMetadata := map[string]any{
 		"source": "./testdata/test.txt",
 	}
 
-	if !cmp.Equal(docs[0].Metadata, expectedMetadata) {
-		t.Errorf("Meta data form text loader not the same as expected. Got:\n %s\nExpect:%s\n", docs[0].Metadata, expectedMetadata)
-	}
+	assert.Equal(t, docs[0].Metadata, expectedMetadata)
 }
diff --git a/exp/embeddings/openai_test.go b/exp/embeddings/openai_test.go
index 6185a59a..bd5fe109 100644
--- a/exp/embeddings/openai_test.go
+++ b/exp/embeddings/openai_test.go
@@ -3,28 +3,24 @@ package embeddings
 import (
 	"os"
 	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestOpenaiEmbeddings(t *testing.T) {
+	t.Parallel()
+
 	if openaiKey := os.Getenv("OPENAI_API_KEY"); openaiKey == "" {
 		t.Skip("OPENAI_API_KEY not set")
 	}
 	e, err := NewOpenAI()
-	if err != nil {
-		t.Fatal(err)
-	}
+	require.NoError(t, err)
 
 	_, err = e.EmbedQuery("Hello world!")
-	if err != nil {
-		t.Fatal(err)
-	}
+	require.NoError(t, err)
 
 	embeddings, err := e.EmbedDocuments([]string{"Hello world", "The world is ending", "bye bye"})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if len(embeddings) != 3 {
-		t.Errorf("Unexpected number of embeddings. Got: %v. Expect 3", len(embeddings))
-	}
+	require.NoError(t, err)
+	assert.Len(t, embeddings, 3)
 }
diff --git a/exp/memory/buffer_memory_test.go b/exp/memory/buffer_memory_test.go
index 67988a9d..8386bea6 100644
--- a/exp/memory/buffer_memory_test.go
+++ b/exp/memory/buffer_memory_test.go
@@ -3,55 +3,43 @@ package memory
 import (
 	"testing"
 
-	"github.com/google/go-cmp/cmp"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"github.com/tmc/langchaingo/schema"
 )
 
 func TestBufferMemory(t *testing.T) {
+	t.Parallel()
+
 	m := NewBufferMemory()
 	result1 := m.LoadMemoryVariables(map[string]any{})
 	expected1 := map[string]any{"history": ""}
-
-	if !cmp.Equal(result1, expected1) {
-		t.Errorf("Empty buffer memory loaded memory variables not equal expected. Got: %v, Wanted: %v", result1, expected1)
-	}
+	assert.Equal(t, expected1, result1)
 
 	err := m.SaveContext(map[string]any{"foo": "bar"}, map[string]any{"bar": "foo"})
-	if err != nil {
-		t.Errorf("Unexpected error: %e", err)
-	}
+	require.NoError(t, err)
 
 	result2 := m.LoadMemoryVariables(map[string]any{})
-	if err != nil {
-		t.Errorf("Unexpected error: %e", err)
-	}
+	require.NoError(t, err)
 
 	expected2 := map[string]any{"history": "Human: bar\nAI: foo"}
-
-	if !cmp.Equal(result2, expected2) {
-		t.Errorf("Buffer memory with messages loaded memory variables not equal expected. Got: %v, Wanted: %v", result2, expected2)
-	}
+	assert.Equal(t, expected2, result2)
 }
 
 func TestBufferMemoryReturnMessage(t *testing.T) {
+	t.Parallel()
+
 	m := NewBufferMemory()
 	m.ReturnMessages = true
 	result1 := m.LoadMemoryVariables(map[string]any{})
 	expected1 := map[string]any{"history": []schema.ChatMessage{}}
-
-	if !cmp.Equal(result1, expected1) {
-		t.Errorf("Empty buffer memory with return messages true loaded memory variables not equal expected. Got: %v, Wanted: %v", result1, expected1)
-	}
+	assert.Equal(t, expected1, result1)
 
 	err := m.SaveContext(map[string]any{"foo": "bar"}, map[string]any{"bar": "foo"})
-	if err != nil {
-		t.Errorf("Unexpected error: %e", err)
-	}
+	require.NoError(t, err)
 
 	result2 := m.LoadMemoryVariables(map[string]any{})
-	if err != nil {
-		t.Errorf("Unexpected error: %e", err)
-	}
+	require.NoError(t, err)
 
 	expectedChatHistory := NewChatMessageHistory(
 		WithPreviousMessages([]schema.ChatMessage{
@@ -61,13 +49,12 @@ func TestBufferMemoryReturnMessage(t *testing.T) {
 	)
 
 	expected2 := map[string]any{"history": expectedChatHistory.GetMessages()}
-
-	if !cmp.Equal(result2, expected2) {
-		t.Errorf("Buffer memory with return messages true and messages loaded memory variables not equal expected. Got: %v, Wanted: %v", result2, expected2)
-	}
+	assert.Equal(t, expected2, result2)
 }
 
 func TestBufferMemoryWithPreLoadedHistory(t *testing.T) {
+	t.Parallel()
+
 	m := NewBufferMemory()
 	m.ChatHistory = NewChatMessageHistory(
 		WithPreviousMessages([]schema.ChatMessage{
@@ -78,8 +65,5 @@ func TestBufferMemoryWithPreLoadedHistory(t *testing.T) {
 
 	result := m.LoadMemoryVariables(map[string]any{})
 	expected := map[string]any{"history": "Human: bar\nAI: foo"}
-
-	if !cmp.Equal(result, expected) {
-		t.Errorf("Buffer memory with messages pre loaded, loaded memory variables not equal expected. Got: %v, Wanted: %v", result, expected)
-	}
+	assert.Equal(t, expected, result)
 }
diff --git a/exp/output_parsers/comma_seperated_parser_test.go b/exp/output_parsers/comma_seperated_parser_test.go
index ad11bc54..b790af40 100644
--- a/exp/output_parsers/comma_seperated_parser_test.go
+++ b/exp/output_parsers/comma_seperated_parser_test.go
@@ -3,40 +3,36 @@ package output_parsers
 import (
 	"testing"
 
-	"github.com/google/go-cmp/cmp"
+	"github.com/stretchr/testify/assert"
 )
 
-type testCase struct {
-	input          string
-	expectedOutput []string
-}
+func TestCommaSeparatedListOutputParser(t *testing.T) {
+	t.Parallel()
 
-var tests = []testCase{
-	{
-		input:          "foo, bar, baz",
-		expectedOutput: []string{"foo", "bar", "baz"},
-	},
-	{
-		input:          "	foo, bar  , b az ",
-		expectedOutput: []string{"foo", "bar", "b az"},
-	},
-	{
-		input:          " foo bar  , baz",
-		expectedOutput: []string{"foo bar", "baz"},
-	},
-}
+	type testCase struct {
+		input          string
+		expectedOutput []string
+	}
 
-func TestCommaSeparatedListOutputParser(t *testing.T) {
-	parser := NewCommaSeparatedList()
+	tests := []testCase{
+		{
+			input:          "foo, bar, baz",
+			expectedOutput: []string{"foo", "bar", "baz"},
+		},
+		{
+			input:          "	foo, bar  , b az ",
+			expectedOutput: []string{"foo", "bar", "b az"},
+		},
+		{
+			input:          " foo bar  , baz",
+			expectedOutput: []string{"foo bar", "baz"},
+		},
+	}
 
+	parser := NewCommaSeparatedList()
 	for _, test := range tests {
 		output, err := parser.Parse(test.input)
-		if err != nil {
-			t.Logf("Unexpected error %s", err.Error())
-		}
-
-		if !cmp.Equal(output, test.expectedOutput) {
-			t.Logf("Parsing with comma separated list did not get expected. Got: %v. Expected %v", output, test.expectedOutput)
-		}
+		assert.NoError(t, err)
+		assert.Equal(t, test.expectedOutput, output)
 	}
 }
diff --git a/exp/output_parsers/structured_parser_test.go b/exp/output_parsers/structured_parser_test.go
index a67a631d..bc9aef4c 100644
--- a/exp/output_parsers/structured_parser_test.go
+++ b/exp/output_parsers/structured_parser_test.go
@@ -3,28 +3,26 @@ package output_parsers
 import (
 	"testing"
 
-	"github.com/google/go-cmp/cmp"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestStructuredOutputParserFromNameAndDescription(t *testing.T) {
+	t.Parallel()
+
 	parser := NewStructuredFromNameAndDescription(map[string]string{"url": "A link to the resource"})
 
 	instruction := parser.GetFormatInstructions()
-	expectedInstruction := "The output should be a markdown code snippet formatted in the following schema: \n```json\n{\n\t\"url\": string // A link to the resource\n}\n```"
-	if instruction != expectedInstruction {
-		t.Errorf("StructuredOutputParser format instruction is not equal expected. \n Got:\n %s Expected: \n %s", instruction, expectedInstruction)
-	}
+	expectedInstruction := "" +
+		"The output should be a markdown code snippet formatted in the following schema" +
+		": \n```json\n{\n\t\"url\": string // A link to the resource\n}\n```"
+	assert.Equal(t, expectedInstruction, instruction)
 
 	llmOutput := "```json\n{\n\t\"url\": \"https://google.com\" \n}\n```"
 
 	parsed, err := parser.Parse(llmOutput)
-	if err != nil {
-		t.Errorf("Unexpected error parsing: %e", err)
-	}
+	require.NoError(t, err)
 
 	expectedParsed := map[string]string{"url": "https://google.com"}
-
-	if !cmp.Equal(parsed, expectedParsed) {
-		t.Errorf("StructuredOutputParser parsing result not equal expected: Got: %v. Expected: %v", parsed, expectedParsed)
-	}
+	assert.Equal(t, expectedParsed, parsed)
 }
diff --git a/exp/prompts/chat_test.go b/exp/prompts/chat_test.go
index 7562f25e..d894b128 100644
--- a/exp/prompts/chat_test.go
+++ b/exp/prompts/chat_test.go
@@ -3,25 +3,25 @@ package prompts
 import (
 	"testing"
 
-	"github.com/google/go-cmp/cmp"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"github.com/tmc/langchaingo/schema"
 )
 
 func TestChatTemplate(t *testing.T) {
+	t.Parallel()
+
 	systemPrompt, err := NewPromptTemplate("Here's some context: {context}", []string{"context"})
-	if err != nil {
-		t.Fatal(err)
-	}
+	require.NoError(t, err)
 
 	userPrompt, err := NewPromptTemplate("Hello AI. Give me a long response. {question}", []string{"question"})
-	if err != nil {
-		t.Fatal(err)
-	}
+	require.NoError(t, err)
 
-	aiPrompt, err := NewPromptTemplate("Very good question. My answer to {question} is {answer}", []string{"answer", "question"})
-	if err != nil {
-		t.Fatal(err)
-	}
+	aiPrompt, err := NewPromptTemplate(
+		"Very good question. My answer to {question} is {answer}",
+		[]string{"answer", "question"},
+	)
+	require.NoError(t, err)
 
 	messages := []Message{
 		NewSystemMessage(systemPrompt),
@@ -30,37 +30,28 @@ func TestChatTemplate(t *testing.T) {
 	}
 
 	_, err = NewChatTemplate(messages, []string{"answer", "context"})
-	if err == nil {
-		t.Errorf("Expected error creating chat template with too few variables")
-	}
+	require.NoError(t, err)
 
 	_, err = NewChatTemplate(messages, []string{"answer", "context", "question", "foo"})
-	if err == nil {
-		t.Errorf("Expected error creating chat template with too many variables")
-	}
+	require.NoError(t, err)
 
 	chatTemplate, err := NewChatTemplate(messages, []string{"answer", "context", "question"})
-	if err != nil {
-		t.Fatal(err)
-	}
+	require.NoError(t, err)
+
+	chatMessages, err := chatTemplate.FormatPromptValue(map[string]any{
+		"context":  "foo",
+		"question": "bar",
+		"answer":   "foobar",
+	})
+	require.NoError(t, err)
 
-	chatMessages, err := chatTemplate.FormatPromptValue(map[string]any{"context": "foo", "question": "bar", "answer": "foobar"})
-	if err != nil {
-		t.Fatal(err)
-	}
 	expectedChatMessages := []schema.ChatMessage{
 		schema.SystemChatMessage{Text: "Here's some context: foo"},
 		schema.HumanChatMessage{Text: "Hello AI. Give me a long response. bar"},
 		schema.AIChatMessage{Text: "Very good question. My answer to bar is foobar"},
 	}
-	expectedString := `[{"text":"Here's some context: foo"},{"text":"Hello AI. Give me a long response. bar"},{"text":"Very good question. My answer to bar is foobar"}]`
 
-	// use cmp to compare:
-	if !cmp.Equal(chatMessages.ToChatMessages(), expectedChatMessages) {
-		t.Errorf("Chat template format prompt value chat messages not equal to expected. Diff: %s", cmp.Diff(chatMessages.ToChatMessages(), expectedChatMessages))
-	}
-
-	if !(chatMessages.String() == expectedString) {
-		t.Errorf("Chat template format prompt value string not equal to expected.\n Got:\n %v\n Expect:\n %v", chatMessages.String(), expectedString)
-	}
+	expectedString := `[{"text":"Here's some context: foo"},{"text":"Hello AI. Give me a long response. bar"},{"text":"Very good question. My answer to bar is foobar"}]`
+	assert.Equal(t, expectedChatMessages, chatMessages.ToChatMessages())
+	assert.Equal(t, expectedString, chatMessages.String())
 }
diff --git a/exp/prompts/prompt_test.go b/exp/prompts/prompt_test.go
index 19364f9b..1197b10b 100644
--- a/exp/prompts/prompt_test.go
+++ b/exp/prompts/prompt_test.go
@@ -2,80 +2,84 @@ package prompts
 
 import (
 	"testing"
+
+	"github.com/stretchr/testify/assert"
 )
 
-type basicPromptTest struct {
-	template          string
-	inputVariables    []string
-	inputValues       map[string]any
-	expectedFormatted string
-	expectsError      bool
-}
+func TestCreatingAndUsingPromptTemplates(t *testing.T) {
+	t.Parallel()
+
+	type basicPromptTest struct {
+		template          string
+		inputVariables    []string
+		inputValues       map[string]any
+		expectedFormatted string
+		expectsError      bool
+	}
 
-var basicPromptTests = []basicPromptTest{
-	{
-		template:          "foo {var}",
-		inputVariables:    []string{"var"},
-		inputValues:       map[string]any{"var": "value"},
-		expectedFormatted: "foo value",
-		expectsError:      false,
-	},
-
-	{
-		template:          "foo {var} bar foo {var2} bar {var}",
-		inputVariables:    []string{"var", "var2"},
-		inputValues:       map[string]any{"var": "value", "var2": "value2"},
-		expectedFormatted: "foo value bar foo value2 bar value",
-		expectsError:      false,
-	},
-
-	{
-		template:          "foo {{var}}",
-		inputVariables:    []string{},
-		inputValues:       map[string]any{},
-		expectedFormatted: "foo {var}",
-		expectsError:      false,
-	},
-
-	{
-		template:          "{{pre{var}post}}",
-		inputVariables:    []string{"var"},
-		inputValues:       map[string]any{"var": "bar"},
-		expectedFormatted: "{prebarpost}",
-		expectsError:      false,
-	},
-
-	{
-		template:          "foo {var}}",
-		inputVariables:    []string{"var"},
-		inputValues:       map[string]any{"var": "value"},
-		expectedFormatted: "",
-		expectsError:      true,
-	},
-
-	{
-		template:          "{ foo {var}",
-		inputVariables:    []string{"var"},
-		inputValues:       map[string]any{"var": "value"},
-		expectedFormatted: "",
-		expectsError:      true,
-	},
-
-	{
-		template:          "foo {var} is {var2}",
-		inputVariables:    []string{"var"},
-		inputValues:       map[string]any{"var": "value"},
-		expectedFormatted: "",
-		expectsError:      true,
-	},
-}
+	basicPromptTests := []basicPromptTest{
+		{
+			template:          "foo {var}",
+			inputVariables:    []string{"var"},
+			inputValues:       map[string]any{"var": "value"},
+			expectedFormatted: "foo value",
+			expectsError:      false,
+		},
+
+		{
+			template:          "foo {var} bar foo {var2} bar {var}",
+			inputVariables:    []string{"var", "var2"},
+			inputValues:       map[string]any{"var": "value", "var2": "value2"},
+			expectedFormatted: "foo value bar foo value2 bar value",
+			expectsError:      false,
+		},
+
+		{
+			template:          "foo {{var}}",
+			inputVariables:    []string{},
+			inputValues:       map[string]any{},
+			expectedFormatted: "foo {var}",
+			expectsError:      false,
+		},
+
+		{
+			template:          "{{pre{var}post}}",
+			inputVariables:    []string{"var"},
+			inputValues:       map[string]any{"var": "bar"},
+			expectedFormatted: "{prebarpost}",
+			expectsError:      false,
+		},
+
+		{
+			template:          "foo {var}}",
+			inputVariables:    []string{"var"},
+			inputValues:       map[string]any{"var": "value"},
+			expectedFormatted: "",
+			expectsError:      true,
+		},
+
+		{
+			template:          "{ foo {var}",
+			inputVariables:    []string{"var"},
+			inputValues:       map[string]any{"var": "value"},
+			expectedFormatted: "",
+			expectsError:      true,
+		},
+
+		{
+			template:          "foo {var} is {var2}",
+			inputVariables:    []string{"var"},
+			inputValues:       map[string]any{"var": "value"},
+			expectedFormatted: "",
+			expectsError:      true,
+		},
+	}
 
-func TestCreatingAndUsingPromptTemplates(t *testing.T) {
 	for _, test := range basicPromptTests {
 		template, err := NewPromptTemplate(test.template, test.inputVariables)
 		if err != nil {
 			if !test.expectsError {
-				t.Errorf("Unexpected error %s", err.Error())
+				assert.NoError(t, err)
 			}
 
 			continue
@@ -84,66 +88,66 @@ func TestCreatingAndUsingPromptTemplates(t *testing.T) {
 		formatted, err := template.Format(test.inputValues)
 		if err != nil {
 			if !test.expectsError {
-				t.Errorf("Unexpected error %s", err.Error())
+				assert.NoError(t, err)
 			}
 
 			continue
 		}
 
-		if formatted != test.expectedFormatted {
-			t.Errorf("Prompt template (%s) with input variables %v and input values %v, not equal to expected %s when formatted. Got: %s", test.template, test.inputVariables, test.inputValues, test.expectedFormatted, formatted)
-		}
+		assert.Equal(t, test.expectedFormatted, formatted)
 	}
 }
 
-type promptWithOptionsTest struct {
-	template          string
-	inputVariables    []string
-	options           []PromptTemplateOption
-	optionDescription string
-	inputValues       map[string]any
-	expectedFormatted string
-	expectsError      bool
-}
+func TestCreatingAndUsingPromptTemplatesWithOptions(t *testing.T) {
+	t.Parallel()
+
+	type promptWithOptionsTest struct {
+		template          string
+		inputVariables    []string
+		options           []PromptTemplateOption
+		optionDescription string
+		inputValues       map[string]any
+		expectedFormatted string
+		expectsError      bool
+	}
 
-var promptWithOptionsTests = []promptWithOptionsTest{
-	{
-		template:          "foo {var}",
-		inputVariables:    []string{"var"},
-		inputValues:       map[string]any{"var": "value"},
-		options:           []PromptTemplateOption{WithTemplateFormatPrompt("foo")},
-		optionDescription: `WithTemplateFormat("foo")`,
-		expectedFormatted: "",
-		expectsError:      true,
-	},
-
-	{
-		template:          "{par} foo {var}",
-		inputVariables:    []string{"var"},
-		inputValues:       map[string]any{"var": "value"},
-		options:           []PromptTemplateOption{WithPartialVariablesPrompt(map[string]any{"par": "bar"})},
-		optionDescription: `WithPartialVariables(map[string]any{"par": "bar"})`,
-		expectedFormatted: "bar foo value",
-		expectsError:      false,
-	},
-
-	{
-		template:          "{par} foo {var}",
-		inputVariables:    []string{"var"},
-		inputValues:       map[string]any{"var": "value", "par": "foo"},
-		options:           []PromptTemplateOption{WithPartialVariablesPrompt(map[string]any{"par": "bar"})},
-		optionDescription: `WithPartialVariables(map[string]any{"par": "bar"})`,
-		expectedFormatted: "foo foo value",
-		expectsError:      false,
-	},
-}
+	promptWithOptionsTests := []promptWithOptionsTest{
+		{
+			template:          "foo {var}",
+			inputVariables:    []string{"var"},
+			inputValues:       map[string]any{"var": "value"},
+			options:           []PromptTemplateOption{WithTemplateFormatPrompt("foo")},
+			optionDescription: `WithTemplateFormat("foo")`,
+			expectedFormatted: "",
+			expectsError:      true,
+		},
+
+		{
+			template:          "{par} foo {var}",
+			inputVariables:    []string{"var"},
+			inputValues:       map[string]any{"var": "value"},
+			options:           []PromptTemplateOption{WithPartialVariablesPrompt(map[string]any{"par": "bar"})},
+			optionDescription: `WithPartialVariables(map[string]any{"par": "bar"})`,
+			expectedFormatted: "bar foo value",
+			expectsError:      false,
+		},
+
+		{
+			template:          "{par} foo {var}",
+			inputVariables:    []string{"var"},
+			inputValues:       map[string]any{"var": "value", "par": "foo"},
+			options:           []PromptTemplateOption{WithPartialVariablesPrompt(map[string]any{"par": "bar"})},
+			optionDescription: `WithPartialVariables(map[string]any{"par": "bar"})`,
+			expectedFormatted: "foo foo value",
+			expectsError:      false,
+		},
+	}
 
-func TestCreatingAndUsingPromptTemplatesWithOptions(t *testing.T) {
 	for _, test := range promptWithOptionsTests {
 		template, err := NewPromptTemplate(test.template, test.inputVariables, test.options...)
 		if err != nil {
 			if !test.expectsError {
-				t.Errorf("Unexpected error %s", err.Error())
+				assert.NoError(t, err)
 			}
 
 			continue
@@ -152,14 +156,12 @@ func TestCreatingAndUsingPromptTemplatesWithOptions(t *testing.T) {
 		formatted, err := template.Format(test.inputValues)
 		if err != nil {
 			if !test.expectsError {
-				t.Errorf("Unexpected error %s", err.Error())
+				assert.NoError(t, err)
 			}
 
 			continue
 		}
 
-		if formatted != test.expectedFormatted {
-			t.Errorf("Prompt template (%s) with input variables %v, input values %v and options %s, not equal to expected (%s) when formatted. Got: %s", test.template, test.inputVariables, test.inputValues, test.optionDescription, test.expectedFormatted, formatted)
-		}
+		assert.Equal(t, test.expectedFormatted, formatted)
 	}
 }
diff --git a/exp/text_splitters/recursive_character_test.go b/exp/text_splitters/recursive_character_test.go
index 3ff61f4d..d1c56941 100644
--- a/exp/text_splitters/recursive_character_test.go
+++ b/exp/text_splitters/recursive_character_test.go
@@ -3,84 +3,80 @@ package text_splitters
 import (
 	"testing"
 
-	"github.com/google/go-cmp/cmp"
+	"github.com/stretchr/testify/assert"
 	"github.com/tmc/langchaingo/schema"
 )
 
-type test struct {
-	text         string
-	chunkOverlap int
-	chunkSize    int
-	expectedDocs []schema.Document
-}
+func TestRecursiveCharacterSplitter(t *testing.T) {
+	t.Parallel()
 
-var tests = []test{
-	{
-		text:         "Hi.\nI'm Harrison.\n\nHow?\na\nb",
-		chunkOverlap: 1,
-		chunkSize:    20,
-		expectedDocs: []schema.Document{
-			{
-				PageContent: "Hi.\nI'm Harrison.",
-				Metadata: map[string]any{"loc": LOCMetadata{
-					Lines: LineData{
-						From: 1,
-						To:   2,
-					},
-				}},
-			},
-			{
-				PageContent: "How?\na\nb",
-				Metadata: map[string]any{"loc": LOCMetadata{
-					Lines: LineData{
-						From: 4,
-						To:   6,
-					},
-				}},
+	type test struct {
+		text         string
+		chunkOverlap int
+		chunkSize    int
+		expectedDocs []schema.Document
+	}
+
+	tests := []test{
+		{
+			text:         "Hi.\nI'm Harrison.\n\nHow?\na\nb",
+			chunkOverlap: 1,
+			chunkSize:    20,
+			expectedDocs: []schema.Document{
+				{
+					PageContent: "Hi.\nI'm Harrison.",
+					Metadata: map[string]any{"loc": LOCMetadata{
+						Lines: LineData{
+							From: 1,
+							To:   2,
+						},
+					}},
+				},
+				{
+					PageContent: "How?\na\nb",
+					Metadata: map[string]any{"loc": LOCMetadata{
+						Lines: LineData{
+							From: 4,
+							To:   6,
+						},
+					}},
+				},
 			},
 		},
-	},
-	{
-		text:         "Hi.\nI'm Harrison.\n\nHow?\na\nbHi.\nI'm Harrison.\n\nHow?\na\nb",
-		chunkOverlap: 1,
-		chunkSize:    40,
-		expectedDocs: []schema.Document{
-			{
-				PageContent: "Hi.\nI'm Harrison.",
-				Metadata: map[string]any{"loc": LOCMetadata{
-					Lines: LineData{
-						From: 1,
-						To:   2,
-					},
-				}},
-			},
-			{
-				PageContent: "How?\na\nbHi.\nI'm Harrison.\n\nHow?\na\nb",
-				Metadata: map[string]any{"loc": LOCMetadata{
-					Lines: LineData{
-						From: 4,
-						To:   11,
-					},
-				}},
+		{
+			text:         "Hi.\nI'm Harrison.\n\nHow?\na\nbHi.\nI'm Harrison.\n\nHow?\na\nb",
+			chunkOverlap: 1,
+			chunkSize:    40,
+			expectedDocs: []schema.Document{
+				{
+					PageContent: "Hi.\nI'm Harrison.",
+					Metadata: map[string]any{"loc": LOCMetadata{
+						Lines: LineData{
+							From: 1,
+							To:   2,
+						},
+					}},
+				},
+				{
+					PageContent: "How?\na\nbHi.\nI'm Harrison.\n\nHow?\na\nb",
+					Metadata: map[string]any{"loc": LOCMetadata{
+						Lines: LineData{
+							From: 4,
+							To:   11,
+						},
+					}},
+				},
 			},
 		},
-	},
-}
+	}
 
-func TestRecursiveCharacterSplitter(t *testing.T) {
 	splitter := NewRecursiveCharactersSplitter()
-
 	for _, test := range tests {
 		splitter.ChunkOverlap = test.chunkOverlap
 		splitter.ChunkSize = test.chunkSize
 
 		docs, err := CreateDocuments(splitter, []string{test.text}, []map[string]any{})
-		if err != nil {
-			t.Errorf("Unexpected error creating documents with recursive character splitter: %s ", err.Error())
-		}
-
-		if !cmp.Equal(docs, test.expectedDocs) {
-			t.Logf("Result creating documents with recursive character splitter not equal expected. \n Got:\n %v \nWant:\n %v \n", docs, test.expectedDocs)
-		}
+		assert.NoError(t, err)
+		assert.Equal(t, test.expectedDocs, docs)
 	}
 }
diff --git a/exp/vector_stores/pinecone/internal/pineconeClient/pinecone_test.go b/exp/vector_stores/pinecone/internal/pineconeClient/pinecone_test.go
index 4663e933..9da95d2c 100644
--- a/exp/vector_stores/pinecone/internal/pineconeClient/pinecone_test.go
+++ b/exp/vector_stores/pinecone/internal/pineconeClient/pinecone_test.go
@@ -3,13 +3,16 @@ package pineconeClient_test
 import (
 	"context"
 	"os"
-	"reflect"
 	"testing"
 
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"github.com/tmc/langchaingo/exp/vector_stores/pinecone/internal/pineconeClient"
 )
 
 func getEnvironmentAndApiKey(t *testing.T) (string, string) {
+	t.Parallel()
+
 	apiKey := os.Getenv("PINECONE_API_KEY")
 	if apiKey == "" {
 		t.Skip("Must set PINECONE_API_KEY to run test")
@@ -24,6 +27,8 @@ func getEnvironmentAndApiKey(t *testing.T) (string, string) {
 }
 
 func TestUpsertAndQuery(t *testing.T) {
+	t.Parallel()
+
 	environment, apiKey := getEnvironmentAndApiKey(t)
 
 	c, err := pineconeClient.New(
@@ -32,10 +37,7 @@ func TestUpsertAndQuery(t *testing.T) {
 		pineconeClient.WithIndexName("foo"),
 		pineconeClient.WithDimensions(2),
 	)
-	if err != nil {
-		t.Errorf("Unexpected error: %s", err.Error())
-		return
-	}
+	require.NoError(t, err)
 
 	queryVector := []float64{0.0, 0.0}
 	expectedClosest := []float64{0.4, 0.5}
@@ -49,10 +51,7 @@ func TestUpsertAndQuery(t *testing.T) {
 		}),
 		"namespace",
 	)
-	if err != nil {
-		t.Errorf("Unexpected error: %s", err.Error())
-		return
-	}
+	require.NoError(t, err)
 
 	queryResult, err := c.Query(
 		context.Background(),
@@ -60,19 +59,9 @@ func TestUpsertAndQuery(t *testing.T) {
 		1,
 		"namespace",
 	)
-	if err != nil {
-		t.Errorf("Unexpected error %s", err.Error())
-		return
-	}
-
-	if len(queryResult.Matches) != 1 {
-		t.Errorf("Unexpected length of matches: %v", queryResult.Matches)
-		return
-	}
+	require.NoError(t, err)
+	require.Len(t, queryResult.Matches, 1)
 
 	closest := queryResult.Matches[0].Values
-	if !reflect.DeepEqual(expectedClosest, closest) {
-		t.Errorf("Expected closest not closest. Got %v, Want: %v", closest, expectedClosest)
-		return
-	}
+	assert.Equal(t, expectedClosest, closest)
 }