{"action":"created","issue":{"url":"https://api.github.com/repos/mockito/mockito/issues/303","repository_url":"https://api.github.com/repos/mockito/mockito","labels_url":"https://api.github.com/repos/mockito/mockito/issues/303/labels{/name}","comments_url":"https://api.github.com/repos/mockito/mockito/issues/303/comments","events_url":"https://api.github.com/repos/mockito/mockito/issues/303/events","html_url":"https://github.com/mockito/mockito/issues/303","id":110832926,"number":303,"title":"New Mockito stubbing api","user":{"login":"szczepiq","id":24743,"avatar_url":"https://avatars.githubusercontent.com/u/24743?v=3","gravatar_id":"","url":"https://api.github.com/users/szczepiq","html_url":"https://github.com/szczepiq","followers_url":"https://api.github.com/users/szczepiq/followers","following_url":"https://api.github.com/users/szczepiq/following{/other_user}","gists_url":"https://api.github.com/users/szczepiq/gists{/gist_id}","starred_url":"https://api.github.com/users/szczepiq/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/szczepiq/subscriptions","organizations_url":"https://api.github.com/users/szczepiq/orgs","repos_url":"https://api.github.com/users/szczepiq/repos","events_url":"https://api.github.com/users/szczepiq/events{/privacy}","received_events_url":"https://api.github.com/users/szczepiq/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/mockito/mockito/labels/1.*%20incompatible","name":"1.* incompatible","color":"5319e7"},{"url":"https://api.github.com/repos/mockito/mockito/labels/maybe","name":"maybe","color":"f7c6c7"},{"url":"https://api.github.com/repos/mockito/mockito/labels/new%20feature","name":"new feature","color":"009800"}],"state":"open","locked":false,"assignee":{"login":"szczepiq","id":24743,"avatar_url":"https://avatars.githubusercontent.com/u/24743?v=3","gravatar_id":"","url":"https://api.github.com/users/szczepiq","html_url":"https://github.com/szczepiq","followers_url":"https://api.github.com/users/szczepiq/followers","following_url":"https://api.github.com/users/szczepiq/following{/other_user}","gists_url":"https://api.github.com/users/szczepiq/gists{/gist_id}","starred_url":"https://api.github.com/users/szczepiq/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/szczepiq/subscriptions","organizations_url":"https://api.github.com/users/szczepiq/orgs","repos_url":"https://api.github.com/users/szczepiq/repos","events_url":"https://api.github.com/users/szczepiq/events{/privacy}","received_events_url":"https://api.github.com/users/szczepiq/received_events","type":"User","site_admin":false},"assignees":[{"login":"szczepiq","id":24743,"avatar_url":"https://avatars.githubusercontent.com/u/24743?v=3","gravatar_id":"","url":"https://api.github.com/users/szczepiq","html_url":"https://github.com/szczepiq","followers_url":"https://api.github.com/users/szczepiq/followers","following_url":"https://api.github.com/users/szczepiq/following{/other_user}","gists_url":"https://api.github.com/users/szczepiq/gists{/gist_id}","starred_url":"https://api.github.com/users/szczepiq/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/szczepiq/subscriptions","organizations_url":"https://api.github.com/users/szczepiq/orgs","repos_url":"https://api.github.com/users/szczepiq/repos","events_url":"https://api.github.com/users/szczepiq/events{/privacy}","received_events_url":"https://api.github.com/users/szczepiq/received_events","type":"User","site_admin":false}],"milestone":null,"comments":5,"created_at":"2015-10-11T04:32:54Z","updated_at":"2016-09-23T19:16:02Z","closed_at":null,"body":"## Top Mockito priorities (equal importance):\r\n \r\nI. Maximize test's clarity\r\nII. Maximize test's failure output (easy test failure debugging)\r\nIII. Guide the engineer toward cleaner, higher quality tests that are easy to maintain\r\n \r\n## Use cases, in the order of importance.\r\n \r\n1) Unused stubbing is dead code, an irrelevant detail that clutters test's clarity.\r\n2) Unintentionally unused stubbing can make test failures harder to debug.\r\n3) When the stubbed call needs to be verified, it is declared twice in test (violation of DRY principle).\r\n\r\n## Proposed solution\r\n\r\nThe solution consists of multiple changes. Perhaps it's better to show the code first, then describe the changes.\r\n\r\n```java\r\n//consistency with times(x) verification mode:\r\nwhen(list.foo(), times(1)).thenReturn(\"blah\");\r\n//same as:\r\nwhen(list.foo()).thenReturn(\"blah\");\r\n\r\n//consistency with times(x) verification mode:\r\nwhen(list.foo(), times(2)).thenReturn(\"blah\");\r\n//same as:\r\nwhen(list.foo()).thenReturn(\"blah\").thenReturn(\"blah\");\r\n\r\n//only this method is invoked on the mock:\r\nwhen(list.foo(), only()).thenReturn(\"blah\");\r\n\r\n//at least\r\nwhen(list.foo(), atLeast(2)).thenReturn(\"blah\");\r\nwhen(list.foo(), atLeastOnce).thenReturn(\"blah\");\r\n\r\n//at least 0, new 'optional' alias for improved readability\r\nwhen(list.foo(), optional()).thenReturn(\"blah\");\r\n//same as\r\nwhen(list.foo(), atLeast(0)).thenReturn(\"blah\");\r\n\r\n//Rule and Runner validate the stubbings automatically\r\n//For framework integrators, base classes, non-JUnit scenarios\r\n//new API to verify stubbings:\r\nMockito.verifyStubbings(mock1, mock2, mock3);\r\n```\r\n\r\n**Summary:**\r\n\r\n1) All stubbed method calls must be used (realized), otherwise the test fails.\r\n2) Test failure is triggered by JUnit rule or Runner\r\n3) When JUnit rule or runner is not used, new Mockito.verifyStubbings() method can be used. The method is not intended for engineers to use in tests. It's intended for framework integrators or a common base class.\r\n4) \"optional\" stubbings. Consider default stubbings that user may put in test constructor or setup method. Those default stubbings are used by most test methods but not all. This is a legit scenario that must be supported.\r\n5) Since all stubbings must be used (realized), we need a new API to describe how the stubbing is verified or not verified (see \"optional\" stubbings - previous point).\r\n\r\n \r\n## Evaluated approaches\r\n\r\nNot all the options address all use cases.\r\n\r\na) Leave things as they are. Devs have used to the ways Mockito works and they are able to find their way. 'Better' is an enemy of 'good'.\r\nb) Enhance Mockito JUnit runner / Rule so that it can detect unused stubs and:\r\nb1) dump useful stubbing information to the system output (see ConsoleSpammingMockitoJUnitRunner)\r\nb2) include useful stubbing information in the exception message when the test fails (see VerboseMockitoJUnitRunner)\r\nc) Make stubbing API richer by adding the concept of verification\r\n\r\n\r\n1) new api: Mockito.verifyStubs()\r\n\t- JUnit runner/Rule automatically apply above after each test\r\n\t- Automatically applied with first verify() method in the test (including verifyNoMoreInteractions())\r\n\t\r\n2) there is a way to mark stubbing as 'optional', for example:\r\n```java\r\nwhen(list.foo(), Mockito.optional()).thenReturn(\"blah\");\r\n```\r\n\r\n3) by default all stubbings are _not_ optional, examples:\t\r\n```java\r\n//equal meaning:\r\nwhen(list.foo()).thenReturn(\"blah\");\t\r\nwhen(list.foo(), Mockito.atLeastOnce()).thenReturn(\"blah\");\t\r\n\r\n//equal meaning:\r\nwhen(list.foo()).thenReturn(1,2,3);\r\nwhen(list.foo(), Mockito.atLeast(3)).thenReturn(\"blah\");\t\r\n```\r\n4) If there is an exception thrown from the code under test we include extra stubbing information, see options b1 and b2."},"comment":{"url":"https://api.github.com/repos/mockito/mockito/issues/comments/249279164","html_url":"https://github.com/mockito/mockito/issues/303#issuecomment-249279164","issue_url":"https://api.github.com/repos/mockito/mockito/issues/303","id":249279164,"user":{"login":"Sarev0k","id":8388574,"avatar_url":"https://avatars.githubusercontent.com/u/8388574?v=3","gravatar_id":"","url":"https://api.github.com/users/Sarev0k","html_url":"https://github.com/Sarev0k","followers_url":"https://api.github.com/users/Sarev0k/followers","following_url":"https://api.github.com/users/Sarev0k/following{/other_user}","gists_url":"https://api.github.com/users/Sarev0k/gists{/gist_id}","starred_url":"https://api.github.com/users/Sarev0k/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Sarev0k/subscriptions","organizations_url":"https://api.github.com/users/Sarev0k/orgs","repos_url":"https://api.github.com/users/Sarev0k/repos","events_url":"https://api.github.com/users/Sarev0k/events{/privacy}","received_events_url":"https://api.github.com/users/Sarev0k/received_events","type":"User","site_admin":false},"created_at":"2016-09-23T19:16:02Z","updated_at":"2016-09-23T19:16:02Z","body":"I think @ryan-gustafson brings up some great points, I too would prefer setting up and interacting with mocks that *never* return dummy values, and instead throw exceptions at method call time if the mock is interacted with in an unconfigured way.\r\n\r\nIn order to work around problems stemming from mocks returning default values, my team has decided to configure our mocks such that any arguments to methods of the right type will return test values, and then only after the code under test has executed, verify that the mocks were interacted with in the correct way.\r\n\r\nConfiguring our mocks such that only specific arguments return a test value has proved to be too much of a time sync for debugging issues caused by the default behavior of incorrectly configured mocks.\r\n\r\nI think if implemented, @ryan-gustafson's suggestion would greatly simplify my team's test cases."}}
{"actor":{"display_login":"Sarev0k"}}