Grok3和deepseek在测试领域如何落地
Grok3
前几天马斯克发布了 Grok3。
简单试用了一下,有几点感想。
- 速度是真快。除了搜索之外,基本上大部分的问题都是秒回;
- 对中文的理解不错;
- 代码生成能力令人印象深刻;
因为只是简单的体验了一下,感触比较浅薄。
与测试的结合
昨天看到 DHH 在社交媒体上谈论 Grok3。
他表示 Grok3 对他来说最重要的功能是处理测试代码。
这里可以简单的理解为使用 Grok3 进行单元测试和集成测试用例的编写。
这也是我认为目前来说 AI 在测试领域可以迅速落地的点。
特别是使用 AI 进行单元测试用例,理由如下。
- 单元测试用例相对来说依赖比较少,做的事情也相对单纯,适合 AI 进行代码生成;
- 单元测试框架相对来说更新不是非常频繁,哪怕 AI 的数据不是最新,生成的代码可用性也很高;
- 我们可以先写实现代码再让 AI 对我们的实现代码进行测试,这样上下文比较精确,生成的代码基本上是可用信赖的;
- 写单元测试比较痛苦的地方是给用例取名,但对 AI 来说,这其实是小菜一碟;我发现 AI 生成的用例名称比我自己写的要有意义的多;
另外我之前也使用过 github 的 copilot 来进行单元测试用例的编写,总体来说速度和准确性都可以接受。
Grok3 生成的单元测试
这里贴一个 Grok3 生成的测试print("hello")
的测试用例。
平心而论,这个用例逻辑很简单,但是实现起来却不是很容易。
Grok3 非常迅速且准确的完成了这个任务。
我只能说,用例写的真好,比我自己实现的要好很多。
我来为 print("hello")
这段 Python 代码写几个单元测试用例。由于 print() 是内置函数,主要作用是输出到控制台,测试它的行为需要考虑如何捕获输出。我们可以使用 Python 的 unittest 框架,并结合 io.StringIO 和 sys.stdout 重定向来验证输出结果。
以下是几个单元测试用例,假设这段代码被封装在一个函数中:
前提代码
假设我们将 print(“hello”) 放入一个简单的函数:
def print_hello():
print("hello")
单元测试代码
以下是基于 unittest 的测试用例:
import unittest
import sys
from io import StringIO
# 被测试的函数
def print_hello():
print("hello")
class TestPrintHello(unittest.TestCase):
def setUp(self):
# 在每个测试用例前重定向 stdout
self.held_output = StringIO()
sys.stdout = self.held_output
def tearDown(self):
# 在每个测试用例后恢复 stdout
self.held_output.close()
sys.stdout = sys.__stdout__
# 测试用例 1:验证输出内容是否为 "hello"
def test_print_hello_output(self):
print_hello()
output = self.held_output.getvalue().strip()
self.assertEqual(output, "hello")
# 测试用例 2:验证输出不为空
def test_print_hello_not_empty(self):
print_hello()
output = self.held_output.getvalue().strip()
self.assertTrue(len(output) > 0)
# 测试用例 3:验证输出不包含意外内容
def test_print_hello_no_unexpected_content(self):
print_hello()
output = self.held_output.getvalue().strip()
self.assertNotEqual(output, "hi") # 不应输出 "hi"
self.assertNotIn("error", output) # 不应包含 "error"
if __name__ == "__main__":
unittest.main()
测试用例说明
test_print_hello_output
- 目标:验证 print_hello() 输出是否精确为 “hello”。
- 方法:捕获输出并与预期值比较。
test_print_hello_not_empty
- 目标:确保函数有输出,而不是空字符串。
- 方法:检查输出长度大于 0。
test_print_hello_no_unexpected_content
- 目标:确认输出不包含意外内容(如 “hi” 或 “error”)。
- 方法:使用 assertNotEqual 和 assertNotIn 检查。
运行结果
运行这段测试代码时: 所有测试应该通过,因为 print(“hello”) 会稳定输出 “hello”。 如果你修改 print_hello() 为 print(“hi”),则 test_print_hello_output 会失败。
注意事项
sys.stdout 重定向:因为 print() 直接输出到标准输出,我们需要用 StringIO 捕获它。 测试局限:如果 print_hello() 被改为写入文件或不调用 print(),这些测试需要调整。 扩展性:你可以添加更多用例,比如测试换行符 \n(默认 print() 带换行):
def test_print_hello_with_newline(self):
print_hello()
output = self.held_output.getvalue()
self.assertTrue(output.endswith("\n"))
如果你想测试其他场景(比如异常处理、参数化输出),告诉我,我可以进一步扩展!
目前的实践
目前工程化方面,除了 github 的 copilot 和 cursor 等工具之外,很多时候我们都是在做复制和粘贴的工作。
先把实现代码粘贴进 AI 对话框,然后把单元测试用例拷贝出来。
效率不高,而且过程很枯燥,时间长了会让人陷入自我怀疑。
难道我只是复制粘贴的工具么?
所以后面 AI 生成单元测试的实践可能会是:
- 更深层次 IDE 级别的集成,比如一键生成单元测试用例并且运行的能力;
- 框架级别的 AI 集成。在开发框架里集成 AI 生成测试用例的能力,并进行执行;
Deepseek 与单元测试
同样的题目,让 deepseek 去实现print("hello")
的测试用例。
以下是针对 print("hello")
的单元测试用例,使用 Python 的 unittest
框架并捕获标准输出验证结果:
import unittest
from io import StringIO
import sys
from unittest.mock import patch
# 要测试的代码
def hello_world():
print("hello")
# 单元测试类
class TestHelloWorld(unittest.TestCase):
def test_hello_output(self):
# 捕获标准输出
captured_output = StringIO()
sys.stdout = captured_output
# 执行函数
hello_world()
# 恢复标准输出
sys.stdout = sys.__stdout__
# 验证输出结果(注意 print 会自动换行,输出实际为 "hello\n")
self.assertEqual(captured_output.getvalue().strip(), "hello")
# 另一种方法:使用 unittest.mock.patch 直接捕获输出
@patch('sys.stdout', new_callable=StringIO)
def test_hello_output_with_patch(self, mock_stdout):
hello_world()
self.assertEqual(mock_stdout.getvalue().strip(), "hello")
if __name__ == "__main__":
unittest.main()
说明:
捕获输出逻辑:
- 第一种方法手动将
sys.stdout
重定向到StringIO
对象,执行后恢复原输出。 - 第二种方法使用
@patch
装饰器自动模拟sys.stdout
,更简洁且避免资源泄漏。
- 第一种方法手动将
注意换行符:
print("hello")
实际输出"hello\n"
,因此断言时使用.strip()
去掉首尾空白(包括换行符)。
运行测试:
将代码保存为 test_hello.py
,执行以下命令:
python -m unittest test_hello.py
预期结果:
测试通过,输出类似:
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
其实 deepseek R1 也能一次写对,但是用例的完备程度是不如 grok3 的,另外输出速度上 grok3 的优势还是比较明显的。
不过我觉得用 deepseek R1 写单元测试用例应该也没什么问题,用例完备性默认不高的问题可以在 prompt 里面增加引导,有可能会得到不错的效果。
总结
总之目前看来 AI 在代码能力上的表现是可圈可点的。
而代码跟测试结合的最紧密的点就是单元测试。
而开发不写单元测试的原因无非是不会写和没时间写。
现在 ai 代码助手的出现让这两个理由已经站不住脚了。
有理由相信在不远的将来,ai 生成的单元测试用例将是大部分软件项目的标配。