首页 文章 精选 留言 我的

精选列表

搜索[智能扫码],共10000篇文章
优秀的个人博客,低调大师

OpenAgents Workspace 超详细小白装机教程+功能详解,快码住

在一个thread里,多个agent共享资源,高效协作.. 让别人到自己的workspace里替你干活.. 5秒钟把别人训练好的agent加进来,跟你的agent一起协作.. 实时监控多个agent干活进度和任务成果.. 这些功能,OpenAgents Workspace都能帮你实现。 近期,大量试用OpenAgents Workspace的新用户后台留言,希望出一期详细教程,以便熟悉使用流程和功能。 这不,OpenAgents Workspace 从0到1装机流程+好用功能步骤详解来了~ 小白也可以一学就会! 友情提示:OW老手可跳过装机教程,直接看功能详解part, pick你心中的top1功能~ 点点github star→ https://github.com/openagents-org/openagents 另:感谢bary贡献的 OpenAgents desktop& iphone app代码~(期待试用和提意见!) Mac Desktop: https://github.com/openagents-org/openagents/releases/tag/go-v0.2.0 Iphone app可以email openagents团队申请invite. Readme: https://github.com/openagents-org/openagents/tree/develop/packages/go 01 装机教程 注: mac/linux/windows 目前都支持cli和gui两种安装方式。本文用cli演示mac/linux 安装步骤,用gui演示windows安装步骤(windows gui目前有些agent暂不支持,如hermes,详细安装教程见前文:https://mp.weixin.qq.com/s/DxeOnUA3G9tSE_KX3yluJg。受限时可转向win cli。win cli正在维护中) 建议代码小白使用gui安装。 mac/linux (CLI演示) 终端运行命令: # macOS / Linux curl -fsSL https://openagents.org/install.sh * bash 运行 agn,打开交互页面,如下:(有延迟是正常现象,等1min左右即可) 如图,页面成功打开。 配置具体agent。以安装openclaw为例,依次运行: agn install openclaw # install a runtime agn create my-agent --type openclaw # create an instance agn env openclaw --set LLM_API_KEY=sk-... # set credentials agn up # start the daemon 之后,打开对应的workspace链接,即可进入聊天界面。 (全流程动态图) windows(GUI演示) 打开https://github.com/openagents-org/openagents,在readme 找到圈红处,选择windows并点击下载。 在下载处点“保留”。 在跳出的弹窗里,点“仍然保留”。 在本地电脑文件下载处,找到相应launcher,点击启动。(最新版0.7.1,不是最新版需更新) 等几秒后,成功打开launcher,看到dashboard。 打开install,安装自己需要的local agent。已安装的agent会显示绿标installed(如图可见,我已安装OpenClaw和OpenCode)。黄标显示的not installed agent系统暂不支持,后续会陆续支持上。hermes gui不支持,详细支持步骤见前文:https://mp.weixin.qq.com/s/DxeOnUA3G9tSE_KX3yluJg 将一个agent从安装到接入workspace的全流程演示:(以OpenCode为例) a.点击install b.看它在终端帮你装OpenCode(因为我是卸载重装,所以显示Update。首次安装会显示install),完成后点back to install c.等2-3秒,看到绿标installed,即为安装成功。 d.点击左侧Agents,随后点击+New Agent e.找到自己想配的agent type,设置name/directory(optional),然后点create f.填model name,设置对应的api key。点test connection,显示没问题后,save保存。 (test connection没问题显示如图标绿) g.即可看到opencode agent正在运行。点击connect。 h.首次使用点击“+create new workspace”,自主命名后,点击open Workspace。 (test connection没问题显示如图标绿) i.打开workspace,agents右下角绿色圆点表明已接入。在thread里和agent开始聊天吧! 02 功能详解 在一个thread里,多个agent共享文件夹和浏览器,高效协作 这是最常用的功能。agents能自主帮你制作网页、完成从策划到宣发全流程内容等,具体可看前文demo: https://mp.weixin.qq.com/s/x5IN7BCgNU1MSZFi4VAGzw https://mp.weixin.qq.com/s/-RCm7KJ8w41y9uNN_HTywg https://mp.weixin.qq.com/s/jbMfK3V8TF6gUXwgXB7krg Share workspace:让别人到自己的workspace里,帮你干活 好处: 多方协同合作,便捷高效 对方无需token即可使用,体感较好 步骤: 点击页面左侧Actions下面的Share。 填入对方的邮箱。点击add后,对方邮箱即有权限访问你的workspace,一起干活。不限制邮箱添加数量,可添加多个。 对方只需在落地页:https://openagents.org/workspace点击open workspace,接着用被邀请的邮箱sign in,就能看到对应的workspace了。 Connect agents:把别人的agent加进来,跟你的agent一起协作 在你想要别人的agent接进来的workspace界面中,点击Actions下面的copy token。显示copied即为复制成功。 以windows gui接入openclaw为例,让对方在agents-my agents-点击connect 后,点join with token。你需要将copied的token复制给对方,让对方填入。 图示右上角,即为接入成功。 点击对应agent的open workspace buttun,如图,已成功接入openclaw到对应workspace里,聊天正常。 Monitor mode:实时监控多agent 干活进度 同上,点击actions下面的settings,打开monitor mode,点击save。 如图,可以看到不同agent thread的实时进展(当前一个thread正在运行,其他thread静止)thread顺序可调换,比如用star置顶调节。系统默认显示前6个。 Split Browser View: 实时查看agent 执行任务的步骤和结果 类同monitor mode第一步,actions-settings-split browser view。 看到页面上方蓝标显示browser preview已开启,给agent布置任务时,右侧即可实时查看preview。 03 几个功能设计的小细节 页面左下角,可自主切换dark/light mode(程序员友好) 点击每个thread右侧圈红处,即可对当前thread进行rename和star(starred的thread会置顶,按次序排列) (更多功能细节期待挖掘中...) 你是如何使用OpenAgents Workspace的?还希望接入哪些功能?欢迎在评论区留言讨论,也欢迎提 Issue 或在 #328 下留言! Issue tracker: github.com/openagents-org/openagents/issues 资源汇总: GitHub:https://github.com/openagents-org/openagents OpenAgents 官网:https://openagents.org Discord:https://discord.com/invite/openagents

优秀的个人博客,低调大师

【码农教程】单元测试利器——手把手教你使用Mockito

作者:京东零售 秦浩然 从你成为开发人员的那一天起,写单元测试终究是你逃不开的宿命!那开发人员为什么不喜欢写单元测试呢?究其原因,无外乎是依赖。依赖其他的服务、依赖运行的环境、等等,各种依赖都成为了我们写单元测试的绊脚石。那现在有个单元测试利器可以帮我们解决依赖的问题,你愿意使用一下吗?你愿意!那就是我们要学习的Mockito 一、前期准备~ 1、准备工作 <!--mockito依赖--> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>2.7.19</version> <scope>test</scope> </dependency> <!-- junit依赖 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> 2、入门知识 1)Mockito:简单轻量级的做mocking测试的框架; 2)mock对象:在调试期间用来作为真实对象的替代品; 3)mock测试:在测试过程中,对那些不容易构建的对象用一个虚拟对象来代替测试的方法就叫mock测试; 4)stub:打桩,就是为mock对象的方法指定返回值(可抛出异常); 5)verify:行为验证,验证指定方法调用情况(是否被调用,调用次数等); 3、五分钟入门Demo @Test public void test0() { //1、创建mock对象(模拟依赖的对象) final List mock = Mockito.mock(List.class); //2、使用mock对象(mock对象会对接口或类的方法给出默认实现) System.out.println("mock.add result => " + mock.add("first")); //false System.out.println("mock.size result => " + mock.size()); //0 //3、打桩操作(状态测试:设置该对象指定方法被调用时的返回值) Mockito.when(mock.get(0)).thenReturn("second"); Mockito.doReturn(66).when(mock).size(); //3、使用mock对象的stub(测试打桩结果) System.out.println("mock.get result => " + mock.get(0)); //second System.out.println("mock.size result => " + mock.size()); //66 //4、验证交互 verification(行为测试:验证方法调用情况) Mockito.verify(mock).get(Mockito.anyInt()); Mockito.verify(mock, Mockito.times(2)).size(); //5、验证返回的结果(这是JUnit的功能) assertEquals("second", mock.get(0)); assertEquals(66, mock.size()); } 二、让我们开始学习吧! 1、行为验证 • 一旦mock对象被创建了,mock对象会记住所有的交互,然后你就可以选择性的验证你感兴趣的交互,验证不通过则抛出异常。 @Test public void test1() { final List mockList = Mockito.mock(List.class); mockList.add("mock1"); mockList.get(0); mockList.size(); mockList.clear(); // 验证方法被使用(默认1次) Mockito.verify(mockList).add("mock1"); // 验证方法被使用1次 Mockito.verify(mockList, Mockito.times(1)).get(0); // 验证方法至少被使用1次 Mockito.verify(mockList, Mockito.atLeast(1)).size(); // 验证方法没有被使用 Mockito.verify(mockList, Mockito.never()).contains("mock2"); // 验证方法至多被使用5次 Mockito.verify(mockList, Mockito.atMost(5)).clear(); // 指定方法调用超时时间 Mockito.verify(mockList, timeout(100)).get(0); // 指定时间内需要完成的次数 Mockito.verify(mockList, timeout(200).atLeastOnce()).size(); } 2、如何做一些测试桩stub • 默认情况下,所有的函数都有返回值。mock函数默认返回的是null,一个空的集合或者一个被对象类型包装的内置类型,例如0、false对应的对象类型为Integer、Boolean; • 一旦测试桩函数被调用,该函数将会一致返回固定的值; • 对于 static 和 final 方法, Mockito 无法对其 when(…).thenReturn(…) 操作。 @Test public void test2() { //静态导入,减少代码量:import static org.mockito.Mockito.*; final ArrayList mockList = mock(ArrayList.class); // 设置方法调用返回值 when(mockList.add("test2")).thenReturn(true); doReturn(true).when(mockList).add("test2"); System.out.println(mockList.add("test2")); //true // 设置方法调用抛出异常 when(mockList.get(0)).thenThrow(new RuntimeException()); doThrow(new RuntimeException()).when(mockList).get(0); System.out.println(mockList.get(0)); //throw RuntimeException // 无返回方法打桩 doNothing().when(mockList).clear(); // 为回调做测试桩(对方法返回进行拦截处理) final Answer<String> answer = new Answer<String>() { @Override public String answer(InvocationOnMock invocationOnMock) throws Throwable { final List mock = (List) invocationOnMock.getMock(); return "mock.size result => " + mock.size(); } }; when(mockList.get(1)).thenAnswer(answer); doAnswer(answer).when(mockList).get(1); System.out.println(mockList.get(1)); //mock.size result => 0 // 对同一方法多次打桩,以最后一次为准 when(mockList.get(2)).thenReturn("test2_1"); when(mockList.get(2)).thenReturn("test2_2"); System.out.println(mockList.get(2)); //test2_2 System.out.println(mockList.get(2)); //test2_2 // 设置多次调用同类型结果 when(mockList.get(3)).thenReturn("test2_1", "test2_2"); when(mockList.get(3)).thenReturn("test2_1").thenReturn("test2_2"); System.out.println(mockList.get(3)); //test2_1 System.out.println(mockList.get(3)); //test2_2 // 为连续调用做测试桩(为同一个函数调用的不同的返回值或异常做测试桩) when(mockList.get(4)).thenReturn("test2").thenThrow(new RuntimeException()); doReturn("test2").doThrow(new RuntimeException()).when(mockList).get(4); System.out.println(mockList.get(4)); //test2 System.out.println(mockList.get(4)); //throw RuntimeException // 无打桩方法,返回默认值 System.out.println(mockList.get(99)); //null } 3、参数匹配器 • 参数匹配器使验证和测试桩变得更灵活; • 为了合理的使用复杂的参数匹配,使用equals()与anyX() 的匹配器会使得测试代码更简洁、简单。有时,会迫使你重构代码以使用equals()匹配或者实现equals()函数来帮助你进行测试; • 如果你使用参数匹配器,所有参数都必须由匹配器提供; • 支持自定义参数匹配器; @Test public void test3() { final Map mockMap = mock(Map.class); // 正常打桩测试 when(mockMap.get("key")).thenReturn("value1"); System.out.println(mockMap.get("key")); //value1 // 为灵活起见,可使用参数匹配器 when(mockMap.get(anyString())).thenReturn("value2"); System.out.println(mockMap.get(anyString())); //value2 System.out.println(mockMap.get("test_key")); //value2 System.out.println(mockMap.get(0)); //null // 多个入参时,要么都使用参数匹配器,要么都不使用,否则会异常 when(mockMap.put(anyString(), anyInt())).thenReturn("value3"); System.out.println(mockMap.put("key3", 3)); //value3 System.out.println(mockMap.put(anyString(), anyInt())); //value3 System.out.println(mockMap.put("key3", anyInt())); //异常 // 行为验证时,也支持使用参数匹配器 verify(mockMap, atLeastOnce()).get(anyString()); verify(mockMap).put(anyString(), eq(3)); // 自定义参数匹配器 final ArgumentMatcher<ArgumentTestRequest> myArgumentMatcher = new ArgumentMatcher<ArgumentTestRequest>() { @Override public boolean matches(ArgumentTestRequest request) { return "name".equals(request.getName()) || "value".equals(request.getValue()); } }; // 自定义参数匹配器使用 final ArgumentTestService mock = mock(ArgumentTestService.class); when(mock.argumentTestMethod(argThat(myArgumentMatcher))).thenReturn("success"); doReturn("success").when(mock).argumentTestMethod(argThat(myArgumentMatcher)); System.out.println(mock.argumentTestMethod(new ArgumentTestRequest("name", "value"))); // success System.out.println(mock.argumentTestMethod(new ArgumentTestRequest())); //null } 4、执行顺序验证 • 验证执行顺序是非常灵活的-你不需要一个一个的验证所有交互,只需要验证你感兴趣的对象即可; • 你可以仅通过那些需要验证顺序的mock对象来创建InOrder对象; @Test public void test4() { // 验证同一个对象多个方法的执行顺序 final List mockList = mock(List.class); mockList.add("first"); mockList.add("second"); final InOrder inOrder = inOrder(mockList); inOrder.verify(mockList).add("first"); inOrder.verify(mockList).add("second"); // 验证多个对象多个方法的执行顺序 final List mockList1 = mock(List.class); final List mockList2 = mock(List.class); mockList1.get(0); mockList1.get(1); mockList2.get(0); mockList1.get(2); mockList2.get(1); final InOrder inOrder1 = inOrder(mockList1, mockList2); inOrder1.verify(mockList1).get(0); inOrder1.verify(mockList1).get(2); inOrder1.verify(mockList2).get(1); } 5、确保交互(interaction)操作不会执行在mock对象上 • 一些用户可能会在频繁地使用verifyNoMoreInteractions(),甚至在每个测试函数中都用。但是verifyNoMoreInteractions()并不建议在每个测试函数中都使用; • verifyNoMoreInteractions()在交互测试套件中只是一个便利的验证,它的作用是当你需要验证是否存在冗余调用时; @Test public void test5() { // 验证某个交互是否从未被执行 final List mock = mock(List.class); mock.add("first"); verify(mock, never()).add("test5"); //通过 verify(mock, never()).add("first"); //异常 // 验证mock对象没有交互过 final List mock1 = mock(List.class); final List mock2 = mock(List.class); verifyZeroInteractions(mock1); //通过 verifyNoMoreInteractions(mock1, mock2); //通过 verifyZeroInteractions(mock, mock2); //异常 // 注意:可能只想验证前面的逻辑,但是加上最后一行,会导致出现异常。建议使用方法层面的验证,如:never(); // 在验证是否有冗余调用的时候,可使用此种方式。如下: final List mockList = mock(List.class); mockList.add("one"); mockList.add("two"); verify(mockList).add("one"); // 通过 verify(mockList, never()).get(0); //通过 verifyZeroInteractions(mockList); //异常 } 6、使用注解简化mock对象创建 注意!下面这句代码需要在运行测试函数之前被调用,一般放到测试类的基类或者test runner中: MockitoAnnotations.initMocks(this); 也可以使用内置的runner: MockitoJUnitRunner 或者一个rule : MockitoRule; // 代替 mock(ArgumentTestService.class) 创建mock对象; @Mock private ArgumentTestService argumentTestService; // 若改注解修饰的对象有成员变量,@Mock定义的mock对象会被自动注入; @InjectMocks private MockitoAnnotationServiceImpl mockitoAnnotationService; @Test public void test6() { // 注意!下面这句代码需要在运行测试函数之前被调用,一般放到测试类的基类或者test runner中; MockitoAnnotations.initMocks(this); when(argumentTestService.argumentTestMethod(new ArgumentTestRequest())).thenReturn("success"); System.out.println(argumentTestService.argumentTestMethod(new ArgumentTestRequest())); //success System.out.println(mockitoAnnotationService.mockitoAnnotationTestMethod()); //null } 7、监控真实对象(部分mock) • 可以为真实对象创建一个监控(spy)对象。当你使用这个spy对象时真实的对象也会也调用,除非它的函数被stub了; • 尽量少使用spy对象,使用时也需要小心形式,例如spy对象可以用来处理遗留代码; • stub语法中同样提供了部分mock的方法,可以调用真实的方法; 完全mock: 上文讲的内容是完全mock,即创建的mock对象与真实对象无关,mock对象的方法默认都是基本的实现,返回基本类型。可基于接口、实现类创建mock对象。 部分mock: 所谓部分mock,即创建的mock对象时基于真实对象的,mock对象的方法都是默认使用真实对象的方法,除非stub之后,才会以stub为准。基于实现类创建mock对象,否则在没有stub的情况下,调用真实方法时,会出现异常。 注意点: Mockito并不会为真实对象代理函数调用,实际上它会拷贝真实对象。因此如果你保留了真实对象并且与之交互,不要期望从监控对象得到正确的结果。 当你在监控对象上调用一个没有被stub的函数时并不会调用真实对象的对应函数,你不会在真实对象上看到任何效果 @Test public void test7() { // stub部分mock(stub中使用真实调用)。注意:需要mock实现类,否则会有异常 final StubTestService stubTestService = mock(StubTestServiceImpl.class); when(stubTestService.stubTestMethodA("paramA")).thenCallRealMethod(); doCallRealMethod().when(stubTestService).stubTestMethodB(); System.out.println(stubTestService.stubTestMethodA("paramA")); //stubTestMethodA is called, param = paramA System.out.println(stubTestService.stubTestMethodB()); //stubTestMethodB is called System.out.println(stubTestService.stubTestMethodC()); //null // spy部分mock final LinkedList<String> linkedList = new LinkedList(); final LinkedList spy = spy(linkedList); spy.add("one"); spy.add("two"); doReturn(100).when(spy).size(); when(spy.get(0)).thenReturn("one_test"); System.out.println(spy.size()); //100 System.out.println(spy.get(0)); //one_test System.out.println(spy.get(1)); //two // spy可以类比AOP。在spy中,由于默认是调用真实方法,所以第二种写法不等价于第一种写法,不推荐这种写法。 doReturn("two_test").when(spy).get(2); when(spy.get(2)).thenReturn("two_test"); //异常 java.lang.IndexOutOfBoundsException: Index: 2, Size: 2 System.out.println(spy.get(2)); //two_test // spy对象只是真实对象的复制,真实对象的改变不会影响spy对象 final List<String> arrayList = new ArrayList<>(); final List<String> spy1 = spy(arrayList); spy1.add(0, "one"); System.out.println(spy1.get(0)); //one arrayList.add(0, "list1"); System.out.println(arrayList.get(0)); //list1 System.out.println(spy1.get(0)); //one // 若对某个方法stub之后,又想调用真实的方法,可以使用reset(spy) final ArrayList<String> arrayList1 = new ArrayList<>(); final ArrayList<String> spy2 = spy(arrayList1); doReturn(100).when(spy2).size(); System.out.println(spy2.size()); //100 reset(spy2); System.out.println(spy2.size()); //0 } 8、@Mock 和 @Spy的使用 • @Mock 等价于 Mockito.mock(Object.class); • @Spy 等价于 Mockito.spy(obj); 区分是mock对象还是spy对象: Mockito.mockingDetails(someObject).isMock(); Mockito.mockingDetails(someObject).isSpy(); @Mock private StubTestService stubTestService; @Spy private StubTestServiceImpl stubTestServiceImpl; @Spy private StubTestService stubTestServiceImpl1 = new StubTestServiceImpl(); @Test public void test8() { MockitoAnnotations.initMocks(this); // mock对象返回默认 System.out.println(stubTestService.stubTestMethodB()); //null // spy对象调用真实方法 System.out.println(stubTestServiceImpl.stubTestMethodC()); //stubTestMethodC is called System.out.println(stubTestServiceImpl1.stubTestMethodA("spy")); //stubTestMethodA is called, param = spy // 区分是mock对象还是spy对象 System.out.println(mockingDetails(stubTestService).isMock()); //true System.out.println(mockingDetails(stubTestService).isSpy()); //false System.out.println(mockingDetails(stubTestServiceImpl).isSpy()); //true } 9、ArgumentCaptor(参数捕获器)捕获方法参数进行验证。(可代替参数匹配器使用) • 在某些场景中,不光要对方法的返回值和调用进行验证,同时需要验证一系列交互后所传入方法的参数。那么我们可以用参数捕获器来捕获传入方法的参数进行验证,看它是否符合我们的要求。 ArgumentCaptor介绍 通过ArgumentCaptor对象的forClass(Class ArgumentCaptor的Api argument.capture() 捕获方法参数 argument.getValue() 获取方法参数值,如果方法进行了多次调用,它将返回最后一个参数值 argument.getAllValues() 方法进行多次调用后,返回多个参数值 @Test public void test9() { List mock = mock(List.class); List mock1 = mock(List.class); mock.add("John"); mock1.add("Brian"); mock1.add("Jim"); // 获取方法参数 ArgumentCaptor argument = ArgumentCaptor.forClass(String.class); verify(mock).add(argument.capture()); System.out.println(argument.getValue()); //John // 多次调用获取最后一次 ArgumentCaptor argument1 = ArgumentCaptor.forClass(String.class); verify(mock1, times(2)).add(argument1.capture()); System.out.println(argument1.getValue()); //Jim // 获取所有调用参数 System.out.println(argument1.getAllValues()); //[Brian, Jim] } 10、简化 ArgumentCaptor 的创建 @Mock private List<String> captorList; @Captor private ArgumentCaptor<String> argumentCaptor; @Test public void test10() { MockitoAnnotations.initMocks(this); captorList.add("cap1"); captorList.add("cap2"); System.out.println(captorList.size()); verify(captorList, atLeastOnce()).add(argumentCaptor.capture()); System.out.println(argumentCaptor.getAllValues()); } 11、高级特性:自定义验证失败信息 @Test public void test11() { final ArrayList arrayList = mock(ArrayList.class); arrayList.add("one"); arrayList.add("two"); verify(arrayList, description("size()没有调用")).size(); // org.mockito.exceptions.base.MockitoAssertionError: size()没有调用 verify(arrayList, timeout(200).times(3).description("验证失败")).add(anyString()); //org.mockito.exceptions.base.MockitoAssertionError: 验证失败 } 12、高级特性:修改没有测试桩的调用的默认返回值 • 可以指定策略来创建mock对象的返回值。这是一个高级特性,通常来说,你不需要写这样的测试; • 它对于遗留系统来说是很有用处的。当你不需要为函数调用打桩时你可以指定一个默认的answer; @Test public void test12(){ // 创建mock对象、使用默认返回 final ArrayList mockList = mock(ArrayList.class); System.out.println(mockList.get(0)); //null // 这个实现首先尝试全局配置,如果没有全局配置就会使用默认的回答,它返回0,空集合,null,等等。 // 参考返回配置:ReturnsEmptyValues mock(ArrayList.class, Answers.RETURNS_DEFAULTS); // ReturnsSmartNulls首先尝试返回普通值(0,空集合,空字符串,等等)然后它试图返回SmartNull。 // 如果最终返回对象,那么会简单返回null。一般用在处理遗留代码。 // 参考返回配置:ReturnsMoreEmptyValues mock(ArrayList.class, Answers.RETURNS_SMART_NULLS); // 未stub的方法,会调用真实方法。 // 注1:存根部分模拟使用时(mock.getSomething ()) .thenReturn (fakeValue)语法将调用的方法。对于部分模拟推荐使用doReturn语法。 // 注2:如果模拟是序列化反序列化,那么这个Answer将无法理解泛型的元数据。 mock(ArrayList.class, Answers.CALLS_REAL_METHODS); // 深度stub,用于嵌套对象的mock。参考:https://www.cnblogs.com/Ming8006/p/6297333.html mock(ArrayList.class, Answers.RETURNS_DEEP_STUBS); // ReturnsMocks首先尝试返回普通值(0,空集合,空字符串,等等)然后它试图返回mock。 // 如果返回类型不能mocked(例如是final)然后返回null。 mock(ArrayList.class, Answers.RETURNS_MOCKS); // mock对象的方法调用后,可以返回自己(类似builder模式) mock(ArrayList.class, Answers.RETURNS_SELF); // 自定义返回 final Answer<String> answer = new Answer<String>() { @Override public String answer(InvocationOnMock invocation) throws Throwable { return "test_answer"; } }; final ArrayList mockList1 = mock(ArrayList.class, answer); System.out.println(mockList1.get(0)); //test_answer } 三、学习了这么多,牛刀小试一下! 测试实体类 @Data public class User { /** * 姓名,登录密码 */ 持久层DAO public interface UserDao { /** * 根据name查找user * @param name * @return */ User getUserByName(String name); /** * 保存user * @param user * @return */ Integer saveUser(User user); } 业务层Service接口 public interface UserService { /** * 根据name查找user * @param name * @return */ User getUserByName(String name); /** * 保存user * @param user * @return */ Integer saveUser(User user); } 业务层Serive实现类 @Service public class UserServiceImpl implements UserService { //userDao @Autowired private UserDao userDao; /** * 根据name查找user * @param name * @return */ @Override public User getUserByName(String name) { try { return userDao.getUserByName(name); } catch (Exception e) { throw new RuntimeException("查询user异常"); } } /** * 保存user * @param user * @return */ @Override public Integer saveUser(User user) { if (userDao.getUserByName(user.getName()) != null) { throw new RuntimeException("用户名已存在"); } try { return userDao.saveUser(user); } catch (Exception e) { throw new RuntimeException("保存用户异常"); } } } 现在我们的Service写好了,想要单元测试一下,但是Dao是其他人开发的,目前还没有写好,那我们如何测试呢? public class UserServiceTest { /** * Mock测试:根据name查询user */ @Test public void getUserByNameTest() { // mock对象 final UserDao userDao = mock(UserDao.class); final UserServiceImpl userService = new UserServiceImpl(); userService.setUserDao(userDao); // stub调用 final User user = new User(); user.setName("admin"); user.setPassword("pass"); when(userDao.getUserByName("admin")).thenReturn(user); // 执行待测试方法 final User user1 = userService.getUserByName("admin"); System.out.println("查询结果:" + JacksonUtil.obj2json(user1)); //查询结果:{"name":"admin","password":"pass"} // 验证mock对象交互 verify(userDao).getUserByName(anyString()); // 验证查询结果 Assert.assertNotNull("查询结果为空!", user1); Assert.assertEquals("查询结果错误!", "admin", user1.getName()); } /** * Mock测试:保存user */ @Mock private UserDao userDao; @InjectMocks private UserServiceImpl userService; @Test public void saveUserTest() throws Exception{ // 执行注解初始化 MockitoAnnotations.initMocks(this); // mock对象stub操作 final User user = new User(); user.setName("admin"); user.setPassword("pass"); when(userDao.getUserByName("admin")).thenReturn(user).thenReturn(null); when(userDao.saveUser(any(User.class))).thenReturn(1); // 验证用户名重复的情况 try { userService.saveUser(user); throw new Exception(); //走到这里说明验证失败 } catch (RuntimeException e) { System.out.println("重复用户名保存失败-测试通过"); //重复用户名保存失败-测试通过 } verify(userDao).getUserByName("admin"); // 验证正常保存的情况 user.setName("user"); final Integer integer = userService.saveUser(user); System.out.println("保存结果:" + integer); //保存结果:1 Assert.assertEquals("保存失败!", 1, integer.longValue()); verify(userDao).saveUser(any(User.class)); verify(userDao, times(2)).getUserByName(anyString()); } } 根据以上代码我们可以知道,当我们的待测类开发完成而依赖的类的实现还没有开发完成。此时,我们就可以用到我们的Mock测试,模拟我们依赖类的返回值,使我们的待测类与依赖类解耦。这样,我们就可以对我们的待测类进行单元测了。 四、参考文档及进一步学习~ Mockito英文版javadoc:https://javadoc.io/static/org.mockito/mockito-core/3.3.3/org/mockito/Mockito.html Mockito中文文档(部分):https://blog.csdn.net/bboyfeiyu/article/details/52127551#35 Mockito使用教程:https://www.cnblogs.com/Ming8006/p/6297333.html 参数捕获器使用:https://www.journaldev.com/21892/mockito-argumentcaptor-captor-annotation 利用ArgumentCaptor(参数捕获器)捕获方法参数进行验证:https://www.iteye.com/blog/hotdog-916364 改变mock返回值:https://www.huangyunkun.com/2014/10/25/mockito-deep-stub-with-enum/ 五分钟了解Mockito:https://www.iteye.com/blog/liuzhijun-1512780 使用Mockito进行单元测试:https://www.iteye.com/blog/qiuguo0205-1443344 JUnit + Mockito 单元测试:https://blog.csdn.net/zhangxin09/article/details/42422643 Mockito中@Mock与@InjectMock:https://www.cnblogs.com/langren1992/p/9681600.html mockito中两种部分mock的实现,spy、callRealMethod:https://www.cnblogs.com/softidea/p/4204389.html Mockito 中被 Mocked 的对象属性及方法的默认值:https://www.cnblogs.com/fnlingnzb-learner/p/10635250.html 单元测试工具之Mockito:https://blog.csdn.net/qq_32140971/article/details/90598454 引入Mockito测试用@Spy和@Mock:https://blog.csdn.net/message_lx/article/details/83308114 Mockito初探(含实例):https://www.iteye.com/blog/sgq0085-2031319 测试覆盖率统计:https://blog.csdn.net/lvyuan1234/article/details/82836052?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task 测试覆盖率无法统计解决:https://blog.csdn.net/zhanglei082319/article/details/81536398

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册