展开菜单
首页 精品内容 本月促销 装机必备 Windows macOS软件 IOS软件 Android AI PDF教程 专题
全部分类

当前位置:

首页 > 编程开发 > Python自动化测试框架之unittest怎么使用

Python自动化测试框架之unittest怎么使用

1unittest框架unittest是python的单元测试框架,它主要有以下作用:提供用例组织与执行:当你的测试用例只有几条时,可以不必考虑用例的组织,但是,当测试用例达到成百上千条时,大量的测试用例堆砌在一起,就产生了扩展性与维护性等问题,此时需要考虑用例的规范与组织问题了。单元测试框架就是来解决这个问题的。提供丰富的比较方法:在用例执行完之后都需要将实际结果与预期结果进行比较(断言),从而断定用例是否可以顺利通过。单元测试一般会提供丰富的断言方法。例如,判断相等/不相等、包含/不包含、True/F

    1 unittest框架

    unittest 是python 的单元测试框架,它主要有以下作用:

    提供用例组织与执行:当你的测试用例只有几条时,可以不必考虑用例的组织,但是,当测试用例达到成百上千条时,大量的测试用例堆砌在一起,就产生了扩展性与维护性等问题,此时需要考虑用例的规范与组织问题了。单元测试框架就是来解决这个问题的。

    提供丰富的比较方法:在用例执行完之后都需要将实际结果与预期结果进行比较(断言),从而断定用例是否可以顺利通过。单元测试一般会提供丰富的断言方法。例如,判断相等/不相等、包含/不包含、True/False等断言方法。

    提供丰富的日志:当测试用例执行失败时能抛出清晰的失败原因,当所有用例执行完成后能提供丰富的执行结果。例如,总的执行时间,失败用例数,成功用例数等。

    unittest里面有四个很重要的概念,test fixture,test case,test suite,test runner。

    Test Fixture

    对一个测试用例环境的搭建和销毁,就是一个fixture,通过覆盖setUp()和tearDown()方法来实现。

    setUp()方法可以进行测试环境的搭建,比如获取待测试浏览器的驱动,或者如果测试中需要访问数据库,那么可以在setUp()中通过建立数据库连接来进行初始化。

    tearDown()方法进行环境的销毁,可以进行关闭浏览器,关闭数据库连接,清除数据库中产生的数据等操作;Test Case

    一个TestCase的实例就是一个测试用例。测试用例就是一个完整的测试流程,包括测试前准备环境的搭建(setUp)、实现测试过程的代码,以及测试后环境的还原(tearDown)。单元测试(unit test)的本质就在这里,一个测试用例就是一个完整的测试单元,可以对某一个功能进行验证。Test Suite

    一个功能的验证往往需要多个测试用例,可以把多个测试用例集合在一起执行,这个就产生了测试套件TestSuite的概念。Test Suit用来将多个测试用例组装在一起;

    Test Runner测试的执行也是非常重要的一个概念,在unittest框架中,通过TextTestRunner类提供的run()方法来执行test suite/test case。

    from selenium import webdriver
    import unittest
    import time
    import os
    from selenium.common.exceptions import NoAlertPresentException
    from selenium.common.exceptions import NoSuchElementException
    from selenium.webdriver.common.by import By
    class Baidu1(unittest.TestCase):
        def setUp(self):
            print("-----setUp-----")
            self.driver = webdriver.Chrome()
            self.url = "https://www.baidu.com/"
            self.driver.maximize_window()
            time.sleep(3)
        def tearDown(self):
            print("-----tearDown-----")
            self.driver.quit()
        def test_hao(self):
            print("111111111")
            driver = self.driver
            url = self.url
            driver.get(url)
            driver.find_element(By.LINK_TEXT,"hao123").click()
            time.sleep(6)
        def test_hbaidu(self):
            print("22222222")
            driver = self.driver
            url = self.url
            driver.get(url)
            driver.find_element(By.ID,"kw").send_keys("unittest")
            driver.find_element(By.ID,"su").submit()
            time.sleep(5)
            print(driver.title)
            # self.assertNotEqual(driver.title, "百度一下_百度搜索", msg="不相等")
            # self.assertTrue("beautiful"=="beauty", msg="Not Equal!")
            time.sleep(6)
        def saveScreenAsPhoto(self, driver, file_name):
            if not os.path.exists("./image"):
                os.makedirs("./image")
            now = time.strftime("%Y%m%d-%H%M%S", time.localtime(time.time()))
            driver.get_screenshot_as_file("./image/" + now + "-" + file_name)
            time.sleep(3)
            print("3333333")
    if __name__ == "__main__":
        unittest.main()

    这个脚本中的类 Baidu1 继承了unittest.TestCase类,所以它使用了unittest框架来组织测试用例(TestCase)。

    setUp() 和 setDown() 是unittest框架中的测试固件。

    以test_开头命名的方法,是测试方法,在运行整个类的时候会默认执行。

    unittest提供了全局的main()方法,使用它可以方便地将一个单元测试模块变成可以直接运行的测试脚

    本。main()方法搜索所有包含在该模块中以”test"命名的测试方法,并自动执行他们。

    2 批量执行脚本

    2.1 构建测试套件

    当我们增加了被测试功能和相应的测试用例之后,我们就需要把多个测试用例组织在一起执行,这就需要用到上文中提到的测试套件Test Suite

    假设我们已经编写了testbaidu1.py,testbaidu2.py两个文件

    testbaidu2.py

    from selenium import webdriver
    import unittest
    import time
    from selenium.common.exceptions import NoAlertPresentException
    from selenium.common.exceptions import NoSuchElementException
    from selenium.webdriver.common.by import By
    class Baidu2 (unittest.TestCase) :
        def setUp(self):
            self.driver = webdriver.Chrome()
            self.driver.implicitly_wait(30)
            self.base_url = "http://www.baidu.com/"
            self.driver.maximize_window()
            self.verificationErrors=[]
            self.accept_next_alert = True
        def tearDown(self):
            self.driver.quit()
            self.assertEqual([], self.verificationErrors)
        def test_hao(self):
            driver = self.driver
            driver.get(self.base_url)
            driver.find_element(By.LINK_TEXT,"新闻").click()
            time.sleep(6)
            self.assertTrue("123" == "1234", msg="not true")
            time.sleep(3)
        def test_baidusearch(self):
            driver = self.driver
            driver.get(self.base_url)
            driver.find_element(By.ID,"kw").clear()
            driver.find_element(By.ID,"kw").send_keys(u"unittest")
            driver.find_element(By.ID,"su").click()
        time.sleep(6)
        def is_element_present(self, how, what):
            try:
                self.driver.find_element(by=how, value=what)
            except NoSuchElementException as e:
                return False
            return True
        def is_alert_present(self):
            try:
                self.driver.switch_to.alert
            except NoAlertPresentException as e:
                return False
            return True
        def close_alert_and_get_its_text(self):
            try:
                alert = self.driver.switch_to.alert
                alert_text = alert.text
                if self.accept_next_alert:
                    alert.accept()
                else:
                    alert.dismiss()
                return alert_text
            finally: self.accept_next_alert = True
    if __name__ == "__main__":
        unittest.main(verbosity=2)

    addTest()

    TestSuite类的addTest()方法可以把不同的测试类中的测试方法组装到测试套件中,但是addTest()一次只能把一个类里面的一个测试方法组装到测试套件中。方式如下:

    将testbaidu1.py、testbaidu2.py中的测试方法放到一个测试套件中,在testsuite.py中实现。

    testsuite.py

    import unittest
    from testUnittest import testbaidu1
    from testUnittest import testbaidu2
    def createsuite():
        #addTest
        suite = unittest.TestSuite()
        suite.addTest(testbaidu1.Baidu1("test_hao"))
        suite.addTest(testbaidu1.Baidu1("test_hbaidu"))
        suite.addTest(testbaidu2.Baidu2("test_hao"))
        suite.addTest(testbaidu2.Baidu2("test_baidusearch"))
        return suite
    if __name__=="__main__":
        suite = createsuite()
        runner = unittest.TextTestRunner(verbosity=2)
        runner.run(suite)

    但是上述做法有两个不方便的地方,阻碍脚本的快速执行,必须每次修改testsuite.py:

    需要导入所有的相关的py文件,比如 import testbaidu1,每新增一个脚本就需要导入一个

    addTest一次只能增加一个测试方法,如果一个py文件中有10个测试方式,如果都要组装到测试套件中,就需要增加10次

    makeSuite()和TestLoader()的应用

    在unittest 框架中提供了makeSuite() 的方法,makeSuite可以实现把测试用例类内所有的测试case组成的测试套件TestSuite ,unittest 调用makeSuite的时候,只需要把测试类名称传入即可。

    TestLoader 用于创建类和模块的测试套件,一般的情况下,使TestLoader().loadTestsFromTestCase(TestClass) 来加载测试类。

    runall.py

    import unittest,csv
    import os,sys
    import time
    import testbaidu1
    import testbaidu2
    #手工添加案例到套件,
    def createsuite():
        suite = unittest.TestSuite()
        #将测试用例加入到测试容器(套件)中
        suite.addTest(unittest.makeSuite(testbaidu1.Baidu1))
        suite.addTest(unittest.makeSuite(testbaidu2.Baidu2))
        return suite
    '''
    suite1 = unittest.TestLoader().loadTestsFromTestCase(testbaidu1.Baidu1)
    suite2 = unittest.TestLoader().loadTestsFromTestCase(testbaidu2.Baidu2)
    suite = unittest.TestSuite([suite1, suite2])
    return suite
    '''
    if __name__=="__main__":
        suite=createsuite()
        runner = unittest.TextTestRunner(verbosity=2)
        runner.run(suite)

    经过makeSuite()和TestLoader()的引入,我们不用一个py文件测试类,只需要导入一次即可。

    discover()的应用

    discover 是通过递归的方式到其子目录中从指定的目录开始, 找到所有测试模块并返回一个包含它们对象的TestSuite ,然后进行加载与模式匹配唯一的测试文件,discover 参数分别为discover(dir,pattern,top_level_dir=None)

    runall.py—注意路径

    import unittest,csv
    import os,sys
    import time
    #手工添加案例到套件,
    def createsuite():
        discover=unittest.defaultTestLoader.discover("../testUnittest",pattern='test*.py',top_level_dir=None)
        print (discover)
        return discover
    if __name__=="__main__":
        suite=createsuite()
        runner = unittest.TextTestRunner(verbosity=2)
        runner.run(suite)

    2.2 用例的执行顺序

    unittest 框架默认加载测试用例的顺序是根据ASCII 码的顺序,数字与字母的顺序为: 0 ~ 9,A ~ Z,a ~ z 。

    对于测试目录与测试文件来说, unittest 框架同样是按照这个规则来加载测试用例

    2.3 忽略用例执行

    语法:

    @unittest.skip(u'The function was canceled, neglects to perform thecase')
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.support.ui import Select
    from selenium.common.exceptions import NoSuchElementException
    from selenium.common.exceptions import NoAlertPresentException
    import unittest, time, re
    class Baidu1(unittest.TestCase):
    #test fixture,初始化环境
        def setUp(self):
            self.driver = webdriver.Chrome()
            self.driver.implicitly_wait(30)
            self.base_url = "http://www.baidu.com/"
            self.verificationErrors = []
            self.accept_next_alert = True
        @unittest.skip("skipping")
        def test_baidusearch(self):
            driver = self.driver
            driver.get(self.base_url + "/")
            driver.find_element(By.ID,"kw").click()
            driver.find_element(By.ID,"kw").clear()
            driver.find_element(By.ID,"kw").send_keys(u"测试")
            driver.find_element(By.ID,"su").click()
            driver.find_element(By.ID,"su").click()
        def test_hao(self):
            driver = self.driver
            driver.get(self.base_url + "/")
            driver.find_element(By.LINK_TEXT,"hao123").click()
            self.assertEqual(u"hao123_上网从这里开始", driver.title)
    #判断element是否存在,可删除
        def is_element_present(self, how, what):
            try: self.driver.find_element(by=how, value=what)
            except NoSuchElementException as e: return False
            return True
    #判断alert是否存在,可删除
        def is_alert_present(self):
            try: self.driver.switch_to_alert()
            except NoAlertPresentException as e: return False
            return True
    #关闭alert,可删除
        def close_alert_and_get_its_text(self):
            try:
                alert = self.driver.switch_to_alert()
                alert_text = alert.text
                if self.accept_next_alert:
                    alert.accept()
                else:
                    alert.dismiss()
                return alert_text
            finally: self.accept_next_alert = True
    #test fixture,清除环境
        def tearDown(self):
            self.driver.quit()
            self.assertEqual([], self.verificationErrors)
    if __name__ == "__main__":
        #执行用例
        unittest.main()

    3 unittest断言

    自动化的测试中, 对于每个单独的case来说,一个case的执行结果中, 必然会有期望结果与实际结果, 来判断该case是通过还是失败, 在unittest 的库中提供了大量的实用方法来检查预期值与实际值, 来验证case的结果, 一般来说, 检查条件大体分为等价性, 逻辑比较以及其他, 如果给定的断言通过, 测试会继续执行到下一行的代码, 如果断言失败, 对应的case测试会立即停止或者生成错误信息( 一般打印错误信息即可) ,但是不要影响其他的case执行。

    unittest 的单元测试库提供了标准的xUnit 断言方法。下面是一些常用的断言

    断言方法断言描述
    assertEqual(arg1, arg2, msg=None)验证arg1=arg2,不等则fail
    assertNotEqual(arg1, arg2, msg=None)验证arg1 != arg2, 相等则fail
    assertTrue(expr, msg=None)验证expr是true,如果为false,则fail
    assertFalse(expr,msg=None)验证expr是false,如果为true,则fail
    assertIs(arg1, arg2, msg=None)验证arg1、arg2是同一个对象,不是则fail
    assertIsNot(arg1, arg2, msg=None)验证arg1、arg2不是同一个对象,是则fail
    assertIsNone(expr, msg=None)验证expr是None,不是则fail
    assertIsNotNone(expr, msg=None)验证expr不是None,是则fail
    assertIn(arg1, arg2, msg=None)验证arg1是arg2的子串,不是则fail
    assertNotIn(arg1, arg2, msg=None)验证arg1不是arg2的子串,是则fail
    assertIsInstance(obj, cls, msg=None)验证obj是cls的实例,不是则fail
    assertNotIsInstance(obj, cls,msg=None)验证obj不是cls的实例,是则fail

    4 HTML报告生成

    脚本执行完毕之后,还需要看到HTML报告,下面我们就通过HTMLTestRunner.py 来生成测试报告。

    修改runall.py

    import unittest,csv
    import os,sys
    import time
    import HTMLTestRunner
    #手工添加案例到套件,
    def createsuite():
        discover=unittest.defaultTestLoader.discover('../testUnittest',pattern='test*.py',top_level_dir=None)
        print (discover)
        return discover
    if __name__=="__main__":
        curpath=sys.path[0]
        #取当前时间
        now=time.strftime("%Y-%m-%d-%H %M %S",time.localtime(time.time()))
        if not os.path.exists(curpath+'/resultreport'):
            os.makedirs(curpath+'/resultreport')
        filename=curpath+'/resultreport/'+now+'resultreport.html'
        with open(filename,'wb') as fp:
        #出html报告
            runner=HTMLTestRunner.HTMLTestRunner(stream=fp,title=u'测试报告',description=u'用例执行情况',verbosity=2)
            suite=createsuite()
            runner.run(suite)

    5 异常捕捉与错误截图

    用例不可能每一次运行都成功,肯定运行时候有不成功的时候。如果可以捕捉到错误,并且把错误截图保存,这将是一个非常棒的功能,也会给我们错误定位带来方便。

    例如编写一个函数,关键语句为driver.get_screenshot_as_file:

    def savescreenshot(self,driver,file_name):
    if not os.path.exists('./image'):
    	os.makedirs('./image')
    now=time.strftime("%Y%m%d-%H%M%S",time.localtime(time.time()))
    #截图保存
    driver.get_screenshot_as_file('./image/'+now+'-'+file_name)
    time.sleep(1)

    示例:testscreenshot.py

    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.support.ui import Select
    from selenium.common.exceptions import NoSuchElementException
    from selenium.common.exceptions import NoAlertPresentException
    import unittest, time, re
    import os
    class Baidu1(unittest.TestCase):
        #test fixture,初始化环境
        def setUp(self):
            self.driver = webdriver.Chrome()
            self.driver.implicitly_wait(30)
            self.base_url = "http://www.baidu.com/"
            self.verificationErrors = []
            self.accept_next_alert = True
        #测试用例,必须以test开头
        def test_hao(self):
            driver = self.driver
            driver.get(self.base_url + "/")
            driver.find_element(By.LINK_TEXT,"hao123").click()
            time.sleep(2)
            try:
                self.assertEqual(u'hao_上网从这里开始', driver.title)
            except:
                self.savescreenshot(driver,'hao.png')
        #判断element是否存在,可删除
        def is_element_present(self, how, what):
            try: self.driver.find_element(by=how, value=what)
            except NoSuchElementException as e: return False
            return True
        #判断alert是否存在,可删除
        def is_alert_present(self):
            try: self.driver.switch_to_alert()
            except NoAlertPresentException as e: return False
            return True
        #关闭alert,可删除
        def close_alert_and_get_its_text(self):
            try:
                alert = self.driver.switch_to_alert()
                alert_text = alert.text
                if self.accept_next_alert:
                    alert.accept()
                else:
                    alert.dismiss()
                return alert_text
            finally: self.accept_next_alert = True
        #test fixture,清除环境
        def tearDown(self):
            self.driver.quit()
            self.assertEqual([], self.verificationErrors)
        def savescreenshot(self,driver,file_name):
            if not os.path.exists('./image'):
                os.makedirs('./image')
                now=time.strftime("%Y%m%d-%H%M%S",time.localtime(time.time()))
                #截图保存
                driver.get_screenshot_as_file('./image/'+now+'-'+file_name)
                time.sleep(1)
    if __name__ == "__main__":
        #执行用例
        unittest.main()
    '''
    可以增加verbosity参数,例如unittest.main(verbosity=2)
    在主函数中,直接调用main() ,在main中加入verbosity=2 ,这样测试的结果就会显示的更加详细。
    这里的verbosity 是一个选项, 表示测试结果的信息复杂度,有三个值:
    0 ( 静默模式): 你只能获得总的测试用例数和总的结果比如总共100个失败,20 成功80
    1 ( 默认模式): 非常类似静默模式只是在每个成功的用例前面有个“ . ” 每个失败的用例前面有个“F”
    2 ( 详细模式): 测试结果会显示每个测试用例的所有相关的信息
    '''

    6 数据驱动

    之前我们的case都是数据和代码在一起编写。考虑如下场景:

    需要多次执行一个案例,比如baidu搜索,分别输入中文、英文、数字等进行搜索,这时候需要编写3个案例吗?有没有版本一次运行?

    python 的unittest 没有自带数据驱动功能。所以如果使用unittest,同时又想使用数据驱动,那么就可以使用DDT来完成。

    ddt的安装:

    pip install ddt
    python setup.py install

    dd.ddt:

    装饰类,也就是继承自TestCase的类。

    ddt.data:

    装饰测试方法。参数是一系列的值。

    data(value) 一次性传入一个参数data(value1,value2,…) 一次性传入多个参数,需要用@unpack映射data(*解析数据的方法(txt/csv文件))

    ddt.file_data:

    装饰测试方法。参数是文件名。文件可以是json 或者 yaml类型。

    注意,如果文件以”.yml”或者”.yaml”结尾,ddt会作为yaml类型处理,其他所有文件都会作为json文件处理。

    如果文件中是列表,每个列表的值会作为测试用例参数,同时作为测试用例方法名后缀显示。

    如果文件中是字典,字典的key会作为测试用例方法的后缀显示,字典的值会作为测试用例参数。

    ddt.unpack:

    传递的是复杂的数据结构时使用。比如使用元组或者列表,添加unpack之后,ddt会自动把元组或者列表对应到多个参数上。字典也可以这样处理。

    Testddt.py:

    from selenium import webdriver
    from selenium.webdriver.common.by import By
    from selenium.webdriver.common.keys import Keys
    from selenium.webdriver.support.ui import Select
    from selenium.common.exceptions import NoSuchElementException
    from selenium.common.exceptions import NoAlertPresentException
    import unittest, time, re
    import os,sys,csv
    from ddt import ddt, data, unpack ,file_data
    def getCsv(file_name):
        rows=[]
        path=sys.path[0].replace('\testddt','')
        print (path)
        with open(path+'/data/'+file_name,'r+b') as f:
            readers=csv.reader(f,delimiter=',',quotechar='|')
            next(readers,None)
            for row in readers:
                temprows=[]
                for i in row:
                    temprows.append(i.decode('gbk'))
                rows.append(temprows)
                return rows
    #引入ddt
    @ddt
    class Testddt(unittest.TestCase):
        def setUp(self):
            self.driver = webdriver.Chrome()
            self.driver.implicitly_wait(30)
            self.base_url = "http://www.baidu.com"
            self.verificationErrors = []
            self.accept_next_alert = True
        #测试用例,必须以test开头
        #增加ddt数据
        @data('selenium','unittest','Junit')
        #@data(2,3,4)
        #单变更时不使用unpack
        #@data([3, 2], [4, 3], [5, 3])
        # @data(*getCsv('test_baidu_data.csv'))
        #使用file_data需要在cmd窗口下运行,否则找不到文件
        #@file_data('test_data_list.json')
        # @unpack
        def test_hao(self,value,expected_value):
        # def test_hao(self,value):
            driver = self.driver
            driver.get(self.base_url + "/")
            driver.find_element(By.ID,"kw").clear()
            driver.find_element(By.ID,"kw").send_keys(value)
            driver.find_element(By.ID,"su").click()
            time.sleep(2)
            self.assertEqual(expected_value, driver.title)
            print (expected_value)
            print (driver.title)
        #判断element是否存在,可删除
        def is_element_present(self, how, what):
            try: self.driver.find_element(by=how, value=what)
            except NoSuchElementException as e: return False
            return True
        #判断alert是否存在,可删除
        def is_alert_present(self):
            try: self.driver.switch_to_alert()
            except NoAlertPresentException as e: return False
            return True
        #关闭alert,可删除
        def close_alert_and_get_its_text(self):
            try:
                alert = self.driver.switch_to_alert()
                alert_text = alert.text
                if self.accept_next_alert:
                    alert.accept()
                else:
                    alert.dismiss()
                return alert_text
            finally: self.accept_next_alert = True
        #test fixture,清除环境
        def tearDown(self):
            self.driver.quit()
            self.assertEqual([], self.verificationErrors)
        def savescreenshot(self,driver,file_name):
            if not os.path.exists('./image'):
                os.makedirs('./image')
            now=time.strftime("%Y%m%d-%H%M%S",time.localtime(time.time()))
            #截图保存
            driver.get_screenshot_as_file('./image/'+now+'-'+file_name)
            time.sleep(1)
    if __name__ == "__main__":
        #执行用例
        unittest.main()
    本文内容来源于互联网,如有侵权请联系删除。
    作者最新文章
    编程开发 Python
    相关文章 更多
    精品专题 更多
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    IOS软件

    正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

    AI

    正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

    PDF教程

    PDF教程适合刚接触PDF文件的用户,本文整理PDF打开、编辑、转换、合并、压缩等常见基础操作。通过这些教程可以快速了解PDF文件怎么处理,解决办公、学习和资料科整理中的常见问题。使用极轻PDF可在线完成多种PDF操作,适合新手快速上手。

    Mac软件 更多
    灵活计算器
    灵活计算器

    灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    赤友清理大师
    赤友清理大师

    赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    极度公式
    极度公式

    极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    图几
    图几

    图几是一款适用于 macOS 的截图、标注与美化工具,支持离线操作保障隐私。界面整理和高频系统操作被放到一起考虑,桌面或窗口内容一多时,管理起来会更省心。

    密码键盘
    密码键盘

    密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

    思源笔记
    思源笔记

    思源笔记是一款本地笔记软件,提供所见即所得的编辑方式,为长文写作带来顺滑的体验。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    Office 365 简体中文
    Office 365 简体中文

    一款文字处理软件,一种订阅式的跨平台办公软件,基于云平台提供多种服务,通过将 Excel 和 Outlook 等应用与 OneDrive 和 Microsoft Teams 等强大的云服务相结合,Office 365 可让任何人使用任何设备随时随地创建和共享内容。

    WALTR PRO
    WALTR PRO

    WALTR是一款电脑至iOS文件传输转换工具,操作简单,快速实现文件识别与传送。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    CodeExpander
    CodeExpander

    CodeExpander 是一款快捷短语输入增强工具,通过键入缩写自动展开为自定义文段,提升工作效率。任务管理和过程控制会更完整,持续下载、批量同步或需要稳定传输流程的场景会更适合它。

    Mountain Duck
    Mountain Duck

    Mountain Duck 是一款能将多个网盘挂载到本地的工具,像本地磁盘一样使用网盘。清理链路的完整性会更好一些,做应用卸载、残留处理和空间整理时,通常能少走很多手动排查步骤。

    Menuist
    Menuist

    Menuist 是一款面向 macOS 的 Finder 右键菜单增强工具,主要用来补充新建文件、快捷导航等常用操作,让日常文件管理和访问路径时更高效、更顺手。

    Mole
    Mole

    Mole 是一款专为 Mac 设计的深度清理优化工具,涵盖缓存清理、应用管理及实时状态监控等功能。清理链路的完整性会更好一些,做应用卸载、残留处理和空间整理时,通常能少走很多手动排查步骤。

    WINDOWS 更多
    Windows 10
    Windows 10

    Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

    极度公式
    极度公式

    极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    密码键盘
    密码键盘

    密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

    思源笔记
    思源笔记

    思源笔记是一款本地笔记软件,提供所见即所得的编辑方式,为长文写作带来顺滑的体验。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

    傲梅轻松备份
    傲梅轻松备份

    傲梅轻松备份是一款专业易用的数据备份软件,为重要数据提供安全保障。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

    Office 365 简体中文
    Office 365 简体中文

    一款文字处理软件,一种订阅式的跨平台办公软件,基于云平台提供多种服务,通过将 Excel 和 Outlook 等应用与 OneDrive 和 Microsoft Teams 等强大的云服务相结合,Office 365 可让任何人使用任何设备随时随地创建和共享内容。

    Wise Folder Hider Pro
    Wise Folder Hider Pro

    Wise Folder Hider Pro 是一款专业级文件和文件夹隐藏加密软件,为私密数据添加多重保护。高频操作更强调就近处理,浏览、整理和跨目录移动文件时,来回切换和重复点击都会少很多。

    WALTR PRO
    WALTR PRO

    WALTR是一款电脑至iOS文件传输转换工具,操作简单,快速实现文件识别与传送。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

    CodeExpander
    CodeExpander

    CodeExpander 是一款快捷短语输入增强工具,通过键入缩写自动展开为自定义文段,提升工作效率。任务管理和过程控制会更完整,持续下载、批量同步或需要稳定传输流程的场景会更适合它。

    PinStack
    PinStack

    PinStack是一款轻量级的Windows平台剪贴板管理工具,优化您的剪贴板使用体验。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

    Mountain Duck
    Mountain Duck

    Mountain Duck 是一款能将多个网盘挂载到本地的工具,像本地磁盘一样使用网盘。清理链路的完整性会更好一些,做应用卸载、残留处理和空间整理时,通常能少走很多手动排查步骤。

    Seer
    Seer

    Seer是一款在Win平台下的空格键功能增强效率工具,只需轻敲空格键,就能预览几乎任何格式的文件。它更适合把零散的小功能集中起来使用,处理高频琐碎任务时会更省事。