From a5848bbc1a7c04cb76a80e1a758358325222e16a Mon Sep 17 00:00:00 2001 From: Xudong Zheng Date: Mon, 28 Sep 2015 22:47:55 +0800 Subject: [PATCH 1/7] Initial commit --- LICENSE | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + From f0999d485ed767d591c5195f6a668becf9948f92 Mon Sep 17 00:00:00 2001 From: Xudong Zheng Date: Mon, 28 Sep 2015 22:50:55 +0800 Subject: [PATCH 2/7] Initial --- unit-test-mock.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 unit-test-mock.md diff --git a/unit-test-mock.md b/unit-test-mock.md new file mode 100644 index 0000000..d5b0a41 --- /dev/null +++ b/unit-test-mock.md @@ -0,0 +1,6 @@ +# Mock 与 Stub +>在进行单元测试的时候,我们会发现我们要测试的方法会有很多外部依赖,比如:发邮件,进行网络通讯,操作文件系统等等。而我们通常关注的是被测试对象的功能和行为,对于它的依赖,我们仅仅需要关注它们之间的交互,但对依赖的对象是如何执行的具体细节我们并不关注。较为常见的技巧就是使用mock对象或者stub对象来代替真实的依赖。 + +##Mocks aren't stubs +这是软件大师[Martin Fowler](http://martinfowler.com/)的一篇经典博文。Martin大师在文章中详细的解释了Mock与Stub的区别,以及怎样使用它们进行TDD实践,强烈推荐阅读,猛击[这里](http://martinfowler.com/articles/mocksArentStubs.html)阅读原文。 + From 294eafea89ce52654935ed65cc4a15daa754bc16 Mon Sep 17 00:00:00 2001 From: Xudong Zheng Date: Mon, 28 Sep 2015 23:57:30 +0800 Subject: [PATCH 3/7] Working on first part --- unit-test-mock.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/unit-test-mock.md b/unit-test-mock.md index d5b0a41..a08bb0b 100644 --- a/unit-test-mock.md +++ b/unit-test-mock.md @@ -2,5 +2,17 @@ >在进行单元测试的时候,我们会发现我们要测试的方法会有很多外部依赖,比如:发邮件,进行网络通讯,操作文件系统等等。而我们通常关注的是被测试对象的功能和行为,对于它的依赖,我们仅仅需要关注它们之间的交互,但对依赖的对象是如何执行的具体细节我们并不关注。较为常见的技巧就是使用mock对象或者stub对象来代替真实的依赖。 ##Mocks aren't stubs -这是软件大师[Martin Fowler](http://martinfowler.com/)的一篇经典博文。Martin大师在文章中详细的解释了Mock与Stub的区别,以及怎样使用它们进行TDD实践,强烈推荐阅读,猛击[这里](http://martinfowler.com/articles/mocksArentStubs.html)阅读原文。 + +这是软件大师[Martin Fowler](http://martinfowler.com/)的一篇经典博文。Martin大师在文章中详细的解释了Mock与Stub的区别,以及怎样使用它们进行TDD实践等等一系列干货,强烈推荐阅读,猛击[这里](http://martinfowler.com/articles/mocksArentStubs.html)阅读原文。 + +我无意把大师的话再复述一遍,所以在本文中我就聊聊我对Mock与Stub的理解以及一些实践。 + +###相同点 + +先看看两者的相同点吧,非常明确的是,Mock和Stub都可以用来对系统(或者将粒度放小为模块,单元)进行隔离。先看看两者的相同点吧,非常明确的是,Mock和Stub都可以用来对系统(或者将粒度放小为模块,单元)进行隔离。 + +###不同点 + + + From 7343e1a224189fffe1d3089c0456a4d98b6b5913 Mon Sep 17 00:00:00 2001 From: Xudong Zheng Date: Mon, 28 Sep 2015 23:59:58 +0800 Subject: [PATCH 4/7] Working on first part --- unit-test-mock.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/unit-test-mock.md b/unit-test-mock.md index a08bb0b..32bb7ee 100644 --- a/unit-test-mock.md +++ b/unit-test-mock.md @@ -3,9 +3,7 @@ ##Mocks aren't stubs -这是软件大师[Martin Fowler](http://martinfowler.com/)的一篇经典博文。Martin大师在文章中详细的解释了Mock与Stub的区别,以及怎样使用它们进行TDD实践等等一系列干货,强烈推荐阅读,猛击[这里](http://martinfowler.com/articles/mocksArentStubs.html)阅读原文。 - -我无意把大师的话再复述一遍,所以在本文中我就聊聊我对Mock与Stub的理解以及一些实践。 +这是软件大师[Martin Fowler](http://martinfowler.com/)的一篇经典博文。Martin大师在文章中详细的解释了Mock与Stub的区别,以及怎样使用它们进行TDD实践等等一系列干货,强烈推荐阅读,猛击[这里](http://martinfowler.com/articles/mocksArentStubs.html)阅读原文。我无意把大师的话再复述一遍,所以在本文中我就聊聊我对Mock与Stub的理解以及一些实践。 ###相同点 From 8f2bca5507af8f33a8c3f5fd2f8f970cec2d4714 Mon Sep 17 00:00:00 2001 From: Xudong Zheng Date: Wed, 14 Oct 2015 00:06:32 +0800 Subject: [PATCH 5/7] add a new posts --- ...rmockito-would-not-work-on-multi-thread.md | 85 +++++++++++++++++++ unit-test-mock.md | 42 ++++++++- 2 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 powermockito-would-not-work-on-multi-thread.md diff --git a/powermockito-would-not-work-on-multi-thread.md b/powermockito-would-not-work-on-multi-thread.md new file mode 100644 index 0000000..ea0a72c --- /dev/null +++ b/powermockito-would-not-work-on-multi-thread.md @@ -0,0 +1,85 @@ +# 在多线程构建场景下Powermockito无法在不同类中Mock同一个静态方法 + +在修改单元测试的过程中,不幸踩了个坑,发现**Powermockito**的``` PowerMock.mockStatic(ClassThatContainsStaticMethod.class)```在多线程场景下是无法正常工作的,这再次验证了之前ThrougthWorks顾问说的那句话: + +>除非万不得已,或者是Mock遗留系统接口,否则不要使用Powermockito。 + +发生问题的场景是这样的```Class C```有一个静态方法,```Class A```和```Class B```都需要调用这个方法完成一些功能: + +``` +Class C{ + public static SomeObject getSomeObject(){ + [....] + } +} + +Class A { + private SomeObject someObject = C.getSomeObject(); + + [.....] +} + +Class B { + private SomeObject someObject = C.getSomeObject(); + + [.....] +} +``` + +由于在测试中直接调用```C.getSomeObject()```会导致一些不可预期的错误,所以我想对A\B类进行测试就必须使用Mock,于是我那么写: + +``` +Class ATest{ + @Before + public void setUp(){ + PowerMock.mockStatic(C.class) + PowerMock.when(C.C.getSomeObject()).thenReturn(PowerMock.mock(SomeObject.class)) + } + +} + +Class BTest{ + @Before + public void setUp(){ + PowerMock.mockStatic(C.class) + PowerMock.when(C.C.getSomeObject()).thenReturn(PowerMock.mock(SomeObject.class)) + } + +} + +``` + +当我在IDE中分别运行```ATest```或者```BTest```是,我的测试都是能正确运行的,但是当你使用Maven或者其他的构建工具进行多线程测试的时候,你就会发现问题来了。一会是A抛异常,一会是B抛异常,总之就是不能很好的工作。由于我不是Powermockito的专家,所以无法深入的去探究这个问题的原因,但是我想,这应该是和静态方法本身在一个JVM内的唯一性有关,我截取了网上两个解释供参考: + +#### Explanation 1 + +>Without going into details let's look at this code written using with Mockito : + +>```given(mock.doSomethingWith(eq("A"), longThat(...)).thenReturn("C");``` + +>Which is roughly equivalent to : + (***** NEVER use a reference to OngoingStubbing in real test code, it might lead to wrong test code *****) + + +> ```String aString = eq("A"); +Long aLong = longThat(...); +String variableThatGiveReturnType = mock.doSomethingWith(aString, aLong); +BDDOngoingStubbing ongoingStubbing = given(variableThatGiveReturnType); +ongoingStubbing.thenReturn("C");``` + + +>The stubbing is clearly not finished until the last call thenReturn is completed, right. + +>Don't you see the missing link between all those line to actually achieve the stubbing in a fluent way ? ;) + +>Dependening on how you do that, if you don't synchronize this block you won't be able to achieve any correct stubbing, otherwise concurrent access anywhere in this block will garble things in the mock internals. + +>And if you add the fact that the mock might be already used, with it's own concurrent code to use the answers, you end up in with completely messed up internal states. + +>Anyway, always stub before using mocks concurrently. + +#### Explanation 2 + +>For healthy scenarios Mockito plays nicely with threads. For instance, you can run tests in parallel to speed up the build. Also, You can let multiple threads call methods on a shared mock to test in concurrent conditions. Check out a [http://mockito.googlecode.com/svn/tags/latest/javadoc/org/mockito/Mockito.html#22 timeout()] feature for testing concurrency. + +>However Mockito is only thread-safe in healthy tests, that is tests without multiple threads stubbing/verifying a shared mock. Stubbing or verification of a shared mock from different threads is NOT the proper way of testing because it will always lead to intermittent behavior. In general mutable state + assertions in multi-threaded environment lead to random results. If you do stub/verify a shared mock across threads you will face occasional exceptions like: WrongTypeOfReturnValue, etc. diff --git a/unit-test-mock.md b/unit-test-mock.md index 32bb7ee..fb912c4 100644 --- a/unit-test-mock.md +++ b/unit-test-mock.md @@ -11,6 +11,42 @@ ###不同点 - - - +Mock和Stub有两个主要区别: + +1. 校验测试结果的方式不同,Mock倾向于校验行为(Beahavior verification),Stub倾向于校验状态; +2. Mock和Stub也代表了两种将测试与设计结合在一起的理念。 + +上面的说法比较抽象,让我们通过例子来看看Mock与Stub的区别。 + +##使用Stub进行单元测试 + +下面是一个使用Stub进行单元测试的例子,我们打算创建一个订单对象,并用仓库中的货物填充这个订单。这个订单对象很简单,只有产品和数量两种信息,仓库保存着不同产品的目录。当我们需要填充订单的时候,会有两种不同的回应,如果仓库中有足够的货物,那么订单就会被填满,并且仓库相应产品的数量就会降低到对应的数量。如果仓库中没有足够的参评,那么订单就不会被填充,并且仓库中产品的数量没有任何的变化。 + +``` +public class OrderStateTester extends TestCase{ + private static String TALISKER = "Talisker"; + private static String HIGHLAND_PARK = "Highland Park"; + private WareHouse warehouse = new WareHouseImpl(); + + protected void setup() throws Exception{ + warehouse.add(TALISKER , 50); + warehouse.add(HIGHLAND_PARK , 25); + } + + public void testOrderIsFilledIfEnoughInWarehouse(){ + Order order = new Order(TALISKER , 50); + order.fill(warehouse); + assertTrue(order.isFilled()); + assertEquals(0 , warehouse.getInventory(TALISKER)); + } + + public void testOrderDoseNotRemoveIfNotEnough() { + Order order = new Order(TALISKER , 51); + order.fill(warehouse); + assertFalse(order.isFilled()); + assertEquals(50 , warehouse.getInventory(TALISKER)); + } +} +``` + +上面的例子里,我们需要对Order对象进行测试,为了验证Order.fill方法,我们还需要WareHouse对象。但真正的WareHouse对象内部可能有很复杂的实现,比如读取文件,访问数据库,持有同步锁以维持对象在并发访问时内部数据正确等。实际上在单元测试时我们并不需要去和这些代码发生交互,而且这些复杂的代码还会让我们的单元测试很不稳定。数据库连接失败、必须的配置文件读取失败等都会导致我们的单元测试失败。显然我们并不希望这些外部的因素影响我们的单元测试 \ No newline at end of file From d27e93aa68bde08db9c3f611d7f3ad169af848bf Mon Sep 17 00:00:00 2001 From: Xudong Zheng Date: Mon, 1 Aug 2016 22:03:52 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E5=A2=9E=E5=8A=A0completionService?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=8A=80=E5=B7=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- java-CompletionService-tips.md | 92 ++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 java-CompletionService-tips.md diff --git a/java-CompletionService-tips.md b/java-CompletionService-tips.md new file mode 100644 index 0000000..c7237b2 --- /dev/null +++ b/java-CompletionService-tips.md @@ -0,0 +1,92 @@ +# CompletionService小技巧 + +在上一篇blogs中,我详细的解释了`CompletionService`的使用方法和`ExecutorCompletionService`的详细实现,这篇blogs中,我就介绍使用它的一个小技巧,算是对上一篇blogs的一个补完。在开始之前我们先回顾一下它的实现。 + +首先,在初始化`ExecutorCompletionService`的时候我们需要传入一个`Executor`,作为`ExecutorCompletionService`执行任务的容器。 + +``` +public ExecutorCompletionService(Executor executor) { + ... +} + +public ExecutorCompletionService(Executor executor, + BlockingQueue> completionQueue) { + ... +} + +``` +然后,调用`submit`方法,向它提交任务。`submit`方法会将我们提交的任务包装成一个`QueueingFuture`并提交给`Executor`来执行。 + +``` +public Future submit(Callable task) { + if (task == null) throw new NullPointerException(); + RunnableFuture f = newTaskFor(task); + executor.execute(new QueueingFuture(f)); + return f; +} +``` + +接着,`QueueingFuture`会在任务执行完成后把执行结果放到队列中。 + +``` +private class QueueingFuture extends FutureTask { + QueueingFuture(RunnableFuture task) { + super(task, null); + this.task = task; + } + protected void done() { completionQueue.add(task); } + private final Future task; +} +``` + +最后,我们通过`take`或者`poll`方法就能拿到任务执行的结果。 + +=== +下面让我们设想一个场景,我需要从网络上下载几张图片和视频并最后把它们渲染到页面上去,由于下载图片和视频都比较耗时,所以我希望能以多线程的形式进行下载。但是由于资源有限,下载的并发度不能太大,所以需要限制线程池的并发线程大小。但如果将可用线程数平均分给下载图片和下载视频的线程池,当某线程池的所有任务执行完成后,另外一个线程池也无法获取到它所释放的资源。那怎么办呢?我们可以创建一个统一的线程池,然后把两个CompletionService绑定上去,让CompletionService作为一个句柄来使用。 + +``` +final ExecutorService pool = Executors.newFixedThreadPool(5); + +final ExecutorCompletionService imageCompletionService = new ExecutorCompletionService<>(pool); +for (final String site : imageSites) { + completionService.submit(new Callable() { + @Override + public String call() throws Exception { + return IOUtils.toString(new URL("http://" + site), StandardCharsets.UTF_8); + } + }); +} + +final ExecutorCompletionService