{"id":879,"date":"2014-02-05T06:57:54","date_gmt":"2014-02-05T13:57:54","guid":{"rendered":"http:\/\/10kdev.net\/?p=879"},"modified":"2014-02-05T08:58:16","modified_gmt":"2014-02-05T15:58:16","slug":"jmockit-with-a-static-a-thread-and-a-singleton","status":"publish","type":"post","link":"http:\/\/10kdev.net\/?p=879","title":{"rendered":"JMockit With a Static, a Thread, and a Singleton"},"content":{"rendered":"<p>Working on a legacy Logging class that uses Thread and a custom Log class singleton (that doesn&#8217;t even extend slf4j.Logger, but uses it. ummm . . . future refactor); the task was givern to me to test it.  Looked in the pom and saw JMockit AND Mockito referenced, and no PowerMock.  There were very few tests, less than 1% for the codebase and neither mock libraries had been implemented in any tests. Knowing there were a lot of final and static classes and other nasty code in this old java app JMockit seemed a good choice because Mockito won&#8217;t test these without Powermock.<\/p>\n<p>The CustomLogger class in question, that has Thread and the Log singleton was something like this:<\/p>\n<pre style=\"white-space: pre-wrap;color:blue;\"><code>\r\npublic final class CustomLogger {\r\n\tpublic static void logStack()\r\n\t{\r\n\t\ttry\r\n\t\t{\r\n\t\t\tMap<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();\r\n\t\t\tprintStack(traces);\r\n\t\t}\r\n\t\tcatch(Error e)  \r\n\t\t{\r\n\t\t\t\/\/<-- HEADACHE!!!! catches junit assertion errors, \r\n\t\t\t\/\/ and all stays green even when things are red\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static String toString(String name, StackTraceElement[] trace)\r\n\t{\r\n\t\tStringBuffer buffer = new StringBuffer();\r\n\t\tbuffer.append(\"Thread \"+name);\r\n\t\tbuffer.append(\"\\n\");\r\n\t\tfor(StackTraceElement element:trace)\r\n\t\t{\r\n\t\t\tbuffer.append(\"\\tat \"+element);\r\n\t\t\tbuffer.append(\"\\n\");\r\n\t\t}\r\n\t\treturn buffer.toString();\r\n\t}\r\n\r\n\tprivate static void printStack(Map<Thread, StackTraceElement[]> traces)\r\n\t{\r\n\t\tStringBuffer buffer = new StringBuffer();\r\n\t\tfor(Thread thread:traces.keySet())\r\n\t\t{\r\n\t\t\tbuffer.append(toString(thread.getName(), traces.get(thread)));\r\n\t\t}\r\n\t\tLog.getInstance().error(buffer.toString()); \/\/ the singleton instance\r\n\t}\r\n}\r\n<\/code><\/pre>\n<p>A LOT of things going on in here.  I decided to mock the Thread and also the underlaying Log singleton.  Things get a little complicated.  On the Log mock I used the validate() technique &#8212; but of course there was a caveat: logStack() has an empty catch block.  This means that method catches any JUnit failures.  I needed a way around it so I decided to throw an actual high level Exception with a message for a failed condition.<\/p>\n<p>I wrote the tests with JUnit\/JMockit on this as such:<\/p>\n<pre style=\"white-space: pre-wrap;color:blue;\"><code>\r\npublic class CustomLoggerTest\r\n{\r\n\r\n\tprivate final StackTraceElement[] traces = new StackTraceElement[2];\r\n\r\n\tprivate final Map<Thread, StackTraceElement[]> stackTraces = new HashMap<Thread, StackTraceElement[]>();\r\n\r\n\tprivate Thread localThread;\r\n\r\n\t@Before\r\n\tpublic void setUp() throws Exception\r\n\t{\r\n\t\ttraces[0] = new StackTraceElement(\"TestClass34\", \"testMethod34\", \"testFile34\", 34);\r\n\t\ttraces[1] = new StackTraceElement(\"TestClass51\", \"testMethod51\", \"testFile51\", 51);\r\n\r\n\t\tThread localThread = new Thread();\r\n\r\n\t\tstackTraces.put(localThread, traces);\r\n\t}\r\n\r\n\t@After\r\n\tpublic void tearDown() throws Exception\r\n\t{\r\n\t\tlocalThread = null;\r\n\t}\r\n\r\n\t@Test\r\n\tpublic void testLogStack()\r\n\t{\r\n\t\tnew Expectations()\r\n\t\t{\r\n\t\t\t@Mocked({ \"getAllStackTraces\" })\r\n\t\t\tfinal Thread unused = null;\r\n\t\t\t{\r\n\t\t\t\tThread.getAllStackTraces();\r\n\t\t\t\tresult = stackTraces;\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tnew Expectations()\r\n\t\t{\r\n\t\t\tLog log;\r\n\t\t\t{\r\n\r\n\t\t\t\tLog.getInstance();  \/\/<-- mocking the singleton\r\n\t\t\t\treturns(log);\r\n\t\t\t\tlog.error(anyString);\r\n\t\t\t\ttimes = 1;\r\n\t\t\t\tforEachInvocation = new Object()\r\n\t\t\t\t{\r\n\t\t\t\t\tvoid validate(Invocation inv, String buffer) throws Exception\r\n\t\t\t\t\t{\t\r\n\t\t\t\t\t\tString expected =\r\n\t\t\t\t\t\t\t\t\"Thread Thread-1\\n\\tat TestClass34.testMethod34(testFile34:34)\\n\\tat \" + \"TestClass51.testMethod51(testFile51:51)\\n\";\r\n\t\t\t\t\t\tSystem.out.println(expected);\t\r\n\t\t\t\t\t\tSystem.out.println(buffer);\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\t\tif (!expected.equalsIgnoreCase(buffer)) {\r\n\t\t\t\t\t\t\t\/\/Assert.fail();  \/\/can't work due to error trapping in method!\r\n\t\t\t\t\t\t\tthrow new Exception(\"Assertion Error: Log Output Not As Expected\");\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t};\r\n\t\t\t}\r\n\t\t};\r\n\t\tCustomLogger.logStack(); \/\/<-- Custom Logger is static\r\n\t}\r\n\r\n}\r\n<\/code><\/pre>\n<p>Side note:  the test coverage in this article isn't complete this is just the most interesting stuff.<\/p>\n<p>Now about that empty empty catch block; that thing cause me some head scratching.  Everytime it went through the assert would throw but the test wouldn't report as RED in a fail situation.  The class can't fail properly because it catches a failed assertEquals. The mechanism in JUnit throws a java.lang.AssertionError or a junit.framework.AssertionFailedError -- trapped by the empty catch block.  The best thing to do would be to fix the code (haha), but those weren't the marching orders.  Please don't get me started.<\/p>\n<p>This goes to show you -- when you are writing codde do NOT ever make *any* assumptions about seemingly harmless methods.  You can get caught inheriting a methoud you may never think gets called (like an equals or hash method_ and it does.<\/p>\n<p>Now about JMockit.  It solved my problem, but I find the documentation not too friendly and the syntax is barely readable to me.  I much, much prefer Mockito.  I am not sure what I think about a mixed JMockit\/Mockito application; I made the decision not to do so on that code base.  But in the future might rethink that -- unit tests should be terse and readable in my opinion, not so cleaver as to require a lot of refactoring.  Self contained per each test is possible, with few harnasses if possible.  This makes things like onboarding and code maintenance MUCH easier.  Who cares, as long as the tests run quickly and they give you meaningful, readable coverage?  They are, after all, the business requirements especially in TDD is a shop's chosen method.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Working on a legacy Logging class that uses Thread and a custom Log class singleton (that doesn&#8217;t even extend slf4j.Logger, but uses it. ummm . . . future refactor); the task was givern to me to test it. Looked in the pom and saw JMockit AND Mockito referenced, and no PowerMock. There were very few [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"http:\/\/10kdev.net\/index.php?rest_route=\/wp\/v2\/posts\/879"}],"collection":[{"href":"http:\/\/10kdev.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/10kdev.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/10kdev.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/10kdev.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=879"}],"version-history":[{"count":8,"href":"http:\/\/10kdev.net\/index.php?rest_route=\/wp\/v2\/posts\/879\/revisions"}],"predecessor-version":[{"id":887,"href":"http:\/\/10kdev.net\/index.php?rest_route=\/wp\/v2\/posts\/879\/revisions\/887"}],"wp:attachment":[{"href":"http:\/\/10kdev.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=879"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/10kdev.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=879"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/10kdev.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=879"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}