专注测试技术的课程订阅站点

完全免费!如何安装openclaw

这几天clawdbot的改名风波闹的沸沸扬扬。

从clawdbot到moltbot再到openclaw,一周装了我3个版本,搞得我都有点精神分裂了。

两个月前,clawdbot的作者peter花了一个周末搞了个叫“WhatsApp Relay”的小项目,结果现在GitHub星标破10万,一周内吸引了200万访问者。完全超出预期。

今天peter正式宣布:这个项目改名叫OpenClaw。

这个名字一路走来挺戏剧:最早叫Clawd,2025年11月诞生,就是那个大模型“Claude”加个爪子的梗,这是英文的谐音梗,我也不太懂,反正logo倒是挺可爱的。

结果火了之后,claude母公司的法务礼貌敲门了,这个名字太像了,蹭了我们的流量,能不能改?

peter表示:我们懂,向资本低头,马上整改。

然后凌晨5点在Discord脑暴,选了Moltbot的名字。molt大概是龙虾要长大就得蜕壳的意思,象征成长。很有诗意,但读起来总有点拗口。

peter不满意,几番折腾之后,现在终于定下来一个新名字:OpenClaw。

这次peter学乖了,商标没有被注册、域名也买好了、代码也迁移了。

现在的名字我觉得是很贴切的:Open代表开源、开放、社区驱动

Claw:就是爪子,保留龙虾爪子的血统,向起点致敬。


这次应该不会再改名字了,终于可以给大家分享一下如何安装openclaw了,对于没有技术基础的同学来说,本地安装还是很有难度的。

下面给大家演示一下如何在mac和linux上安装openclaw,这里我们的目标是让国内的同学不花一分钱把服务运行起来。

YouTube视频

B站视频

mac 系统

先看mac系统。

首先安装nodejs的最新稳定版本。我安装的是24.13.0,推荐用nvm安装,我把命令放这里了。

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
nvm install --lts

Mac上还需要安装 Command Line Tools,命令放在下面了。

# 先彻底删除旧的(很重要,很多时候不删它就装错版本)
sudo rm -rf /Library/Developer/CommandLineTools

# 让系统重新拉取(会弹出安装窗口,按提示点继续)
sudo xcode-select --install

安装git,我们用brew安装。

brew install git

接下来把官网上的命令拷贝下来,在命令行里执行。

测试周刊006: playwrightv1.53的新功能

在一个已有开发节奏的团队中,作为第一位测试人员去推行新流程,绝非易事。

你会被开发质疑:是不是要拖慢上线进度?是不是要卡死流程,让整个开发团队都不爽?

这些成见不是你的错,但你必须面对它们。

pydoll 初体验

之前介绍过一款新的 ui 自动化测试工具—-pydoll

今天抽空在 github 的 copilot 的帮助下试用了一下。

我用 pydoll 实现了一个测试任务列表的测试套件,包含 5 个测试用例。

具体用例如下

import asyncio
import os
import unittest
from pydoll.browser.chromium import Chrome
from pydoll.browser.options import ChromiumOptions
from pydoll.constants import Key

class TestTodoMVC(unittest.IsolatedAsyncioTestCase):
    async def asyncSetUp(self):
        options = ChromiumOptions()
        options.add_argument('--start-maximized')
        options.add_argument('--disable-notifications')
        self.browser = Chrome(options=options)
        self.tab = await self.browser.start()
        await self.tab.go_to('https://todomvc.com/examples/react/dist/')

    async def asyncTearDown(self):
        await self.browser.__aexit__(None, None, None)

    async def test_add_todo(self):
        new_todo = await self.tab.find(class_name='new-todo', timeout=5, raise_exc=True)
        await new_todo.type_text("Install pydoll")
        await asyncio.sleep(1)
        await new_todo.press_keyboard_key(Key.ENTER)
        todo_items = await self.tab.find(class_name = 'view', timeout=5, find_all=True, raise_exc=True)
        found = False
        texts = []
        for item in todo_items:
            text = await item.text
            texts.append(text)
            if "Install pydoll" in text:
                found = True
                break
        self.assertTrue(found)

    async def test_complete_todo(self):
        await self.test_add_todo()
        toggle = await self.tab.find(class_name="toggle", timeout=5, raise_exc=True)
        await toggle.click()
        completed = await self.tab.find(class_name='completed', timeout=5, raise_exc=True)
        self.assertIsNotNone(completed)

    async def test_delete_todo(self):
        await self.test_add_todo()
        todo_item = await self.tab.find(class_name='view', timeout=5, raise_exc=True)
        await todo_item.click()
        destroy_btn = await todo_item.find(class_name='destroy', timeout=5, raise_exc=True)
        await destroy_btn.click()
        todo_items = await self.tab.find(class_name='view', find_all=True)
        found = False
        for item in todo_items:
            if "Install pydoll" in item.text:
                found = True
                break
        self.assertFalse(found)

    async def test_filter_todo(self):
        await self.test_add_todo()
        toggle = await self.tab.find(class_name="toggle", timeout=5, raise_exc=True)
        await toggle.click()
        active_filter = await self.tab.find(text="Active", timeout=5, raise_exc=True)
        await active_filter.click()
        await asyncio.sleep(1)
        active_items = [item for item in await self.tab.find(class_name = 'view', find_all=True)]
        self.assertEqual(len(active_items), 0)
        completed_filter = await self.tab.find(text="Completed", timeout=5, raise_exc=True)
        await completed_filter.click()
        await asyncio.sleep(1)
        completed_items = await self.tab.find(class_name = 'view', find_all=True)
        found = False
        for item in completed_items:
            title = await item.text
            if "Install pydoll" in title:
                found = True
                break
        self.assertTrue(found)

    async def test_screenshot(self):
        await self.test_add_todo()
        screenshot_path = os.path.join(os.getcwd(), 'pydoll_repo.png')
        await self.tab.take_screenshot(path=screenshot_path)
        self.assertTrue(os.path.exists(screenshot_path))

if __name__ == "__main__":
    unittest.main()

上面的代码实现了

测试周刊005: Google是怎么做测试的

在 Google 早期,测试并不是首要任务。公司文化高度依赖工程师的才华——聪明的人写聪明的代码。大多数情况下,这种方式都很有效。少数系统部署了集成测试,但广泛的、结构化的测试极其罕见。这就像软件开发的"狂野西部"时代。