鍘熸枃
鏇存柊鏃堕棿锛�016-01-17 19:25:40
鏂规硶鍖呮嫭鏋勯�鏂规硶鏄�mock 鐨勭洰鏍囥�Mocking 鎻愪緵浜嗘妸琚祴浠g爜鍜屽畠鐨勪緷璧栭殧绂荤殑鏈哄埗銆傛垜浠彲浠ラ�杩囨祴璇曠被鐨勫睘鎬у拰娴嬭瘯鏂规硶鐨勫弬鏁版潵澹版槑 mock 瀵硅薄銆傛墍鏈夌殑寮曠敤绫诲瀷閮藉彲浠ヨ mock锛歩nterface, class (鍖呮嫭 final), annotation 鍜�enum 銆�nbsp;
缂虹渷鐨勶紝绫荤殑鎵�湁鏂规硶鍖呮嫭鏋勯�鏂规硶閮戒細琚玬ock, 涓嶇瀹冩湁private, final, static 绛変换浣曚慨楗扮銆�鍚屾椂锛岀被鐨勬墍鏈夐櫎浜哋bject 涔嬪鐨勬墍鏈夌鍏堢被涔熻mock銆傝mock 鐨勬柟娉曡皟鐢ㄩ兘浼氳閲嶅畾鍚戠粰 JMockit 澶勭悊銆�/p>
涓嬮潰鐨勪唬鐮佹紨绀轰簡浣跨敤mock field 鍜屽弬鏁扮殑鍏稿瀷鏂瑰紡锛�/p>
// "Dependency" is mocked for all tests in this test class. // The "mockInstance" field holds a mocked instance automatically created for use in each test. @Mocked Dependency mockInstance; @Test public void doBusinessOperationXyz(@Mocked final AnotherDependency anotherMock) { ... new Expectations() {{ // an "expectation block" ... // Record an expectation, with a given value to be returned: mockInstance.mockedMethod(...); result = 123; ... }}; ... // Call the code under test. ... new Verifications() {{ // a "verification block" // Verifies an expected invocation: anotherMock.save(any); times = 1; }}; ... }
涓婁緥涓璵ock鐨勫弬鏁版槸鍦↗Mockit鐢熸垚涔嬪悗锛岀敱Junit鎴朤estNG鐨剅unner浼犲叆鐨勩�锛堟樉鐒讹紝杩欏氨鏄负浠�箞JMockit 渚濊禆JUnit鎴朤estNG 鐨勫師鍥狅級
鏈変笁绉峬ock鏍囨敞锛�/p>
娉ㄦ剰杩欓噷鎻愬埌鐨勭被涔熷寘鎷帴鍙c�
涓庡叾浠栫殑mock妗嗘灦涓嶅悓, @Mocked 鍜�@Capturing 缂虹渷mock鐨勬槸绫汇�杩欐剰鍛崇潃锛屼竴鏃︿綘杩欐牱" @Mocked SomeClass ins1" , 鍗充娇浣犺皟鐢ㄦ瀯閫犳柟娉昻ew涓�釜瀵硅薄 ins2锛宨ns2涔熸槸mock瀵硅薄銆�/p>
涓�釜expectation 鏄竴涓粰瀹氱殑娴嬭瘯瑕佽皟鐢ㄧ殑鏂规硶鍖呮嫭鏋勯�鏂规硶鐨勯泦鍚堛�瀹冨彲鑳借缁嗗垪鍑哄鍚屼竴涓柟娉曞娆′笉鍚岀殑璋冪敤锛屼絾杩欎笉鏄繀椤荤殑銆�鍦ㄦ祴璇曢樁娈碉紝琚祴瀵硅薄璋冪敤mock瀵硅薄鐨勬柟娉曠殑杩囩▼瑕佷笌 expectation 鍖归厤锛屼絾鍖归厤鐨勮鍒欎笉浠呬粎闄愪簬鏂规硶鐨勭鍚嶏紝杩樹笌杩愯鏃剁殑鍥犵礌鏈夊叧绯伙細瀵瑰簲鐨刴ock瀹炰緥锛屽弬鏁板�浠ュ強璋冪敤鐨勬鏁扮瓑銆侲xpactation 灏辨槸瑕佹寚瀹氳繖鍑犱釜绾︽潫銆�/p>
鍙傛暟鐨勫尮閰嶏紝鎴戜滑鍙互绮剧‘鍖归厤鍙傛暟鍊硷紝涔熷彲浠ユ澗鏁g殑鍖归厤銆�/p>
涓庡叾浠�mock 妗嗘灦鐨勪笉鍚岋紝瀹氬埗浜や簰琛屼负鍙渶瑕佸湪expectation 鍧楀唴鐩存帴璋冪敤mock瀵硅薄鐨勬柟娉曪紝鏃犻渶鐗规畩鐨�API锛屼絾娉ㄦ剰锛岃繖骞堕潪鐪熺殑璋冪敤浜嗛偅涓柟娉曪紝鍙槸琛ㄨ揪涓�釜"鏈熸湜" :
Test public void doBusinessOperationXyz(@Mocked final Dependency mockInstance) { ... new Expectations() {{ ... // An expectation for an instance method: mockInstance.someMethod(1, "test"); result = "mocked"; ... }}; // A call to code under test occurs here, leading to mock invocations // that may or may not match specified expectations. }
浠讳綍娴嬭瘯鍙互琚垝鍒嗗埌3鍚勯樁娈碉細
@Test public void someTestMethod() { // 1. Preparation: whatever is required before the code under test can be exercised. ... // 2. The code under test is exercised, usually by calling a public method. ... // 3. Verification: whatever needs to be checked to make sure the code exercised by // the test did its job. ... }
杩�涓樁娈典篃鍙互绉颁负 Arrange, Act, Assert 銆�/p>
鍦ㄤ娇鐢╩ock鎶�湳鐨勫熀浜庤涓虹殑娴嬭瘯涓紝杩欎笁涓樁娈垫濂藉搴斿埌锛�/p>
褰撶劧锛孍xpectations 鍧楁垨 Verifacations 鍧楀彲浠ユ湁澶氫釜涔熷彲浠ユ病鏈夈�
Expectations : 鎸囧畾鐨勬柟娉曟垨娆℃暟蹇呴』婊¤冻锛屾病鎸囧畾鐨勯殢鎰忋�浣嗕笉鍏冲績椤哄簭銆�/p>
StrictExpectation : 鎰忓懗鐫�弗鏍煎尮閰嶃� replay 闃舵璋冪敤浜嗗摢鍑犱釜鏂规硶锛岃皟鐢ㄧ殑娆℃暟(娌℃湁鎸囧畾灏辨槸1)鍜岄『搴忥紝閮藉繀椤诲拰 record 闃舵涓�牱锛屽惁鍒欐祴璇昮ail (unexpected invocation)銆�/p>
NonStrictExpectations : 鏃犳墍璋擄紝涔熷氨鏄浉褰撲簬涓嶅仛楠岃瘉锛屽彧鏄敤鏉ユ寚瀹歳esult銆傚缓璁粎琚敤浜巗etup(@Before )闃舵銆�nbsp;
浠ヤ笂涓夎�鐨勫尯鍒剰鍛崇潃瀵逛簬 StrictExpectation 涓嶉渶瑕�Verifications 鍧楋紝鑰孨onStrictExpectations 闇�銆�馃槃缁堜簬鏄庣櫧瀹冧负鍟ュ彨 Expectations 浜嗮煒�/p>
鍦‥xpectations 鍧楀唴锛屽浜�non-void 鐨勬柟娉曪紝绱ц窡鍦ㄦ柟娉曡皟鐢ㄤ箣鍚庣粰 result 璧嬪�鏉ユ寚瀹氳繑鍥炲�銆�/p>
鍙互閫氳繃缁�result 璧�Throwable 鐨勫疄渚嬫潵璁╂柟娉曟姏鍑哄紓甯革紝杩欎篃閫傜敤浜庢瀯閫犳柟娉曘�
瀵逛笌鍚屼竴涓柟娉曡繛缁皟鐢ㄨ�杩斿洖涓嶅悓鐨勫�鎴栧紓甯革紝鏈�绉嶉�鎷╁彲浠ュ姙鍒帮細1锛岀粰result 璧嬪�涓�釜鏁扮粍鎴杔ist ; 2, 璋冪敤 results(Object ...) ; 3, 鍦ㄥ悓涓�杩炵画 瀵箁esult 璧嬪�澶氭銆�nbsp;
鐪嬩笅闈㈣繖涓ず渚嬶細
棣栧厛鏄娴嬬殑绫伙細
public class UnitUnderTest { (1)private final DependencyAbc abc = new DependencyAbc(); public void doSomething() { (2) int n = abc.intReturningMethod(); for (int i = 0; i < n; i++) { String s; try { (3) s = abc.stringReturningMethod(); } catch (SomeCheckedException e) { // somehow handle the exception } // do some other stuff } } }
鍏朵腑 3 涓暟瀛楁槸涓変釜娴嬭瘯鐐癸紝鑰冭檻瑕佸浣�expect 鏋勯�鏂规硶鍙婂锛�锛夌殑杩炵画璋冪敤銆�/p>
娴嬭瘯濡備笅锛�/p>
@Test public void doSomethingHandlesSomeCheckedException(@Mocked final DependencyAbc abc) throws Exception { new Expectations() {{ (1) new DependencyAbc(); (2) abc.intReturningMethod(); result = 3; (3) abc.stringReturningMethod(); returns("str1", "str2"); result = new SomeCheckedException(); }}; new UnitUnderTest().doSomething(); }
@Mocked 浼氫綔鐢ㄤ簬绫荤殑鎵�湁瀹炰緥涓娿� 浣嗘湁鏃舵垜浠渶瑕乿erify鍦ㄦ寚瀹氬疄渚嬩笂鐨勬柟娉曡皟鐢紝鎴栬�鎴戜滑涔熷悓鏃堕渶瑕乵ocked鍜屽疄渚嬨�@Injectable 鐢ㄤ簬婊¤冻杩欎釜瑕佹眰銆�/p>
姝e洜涓篅Injectable 鍙綔鐢ㄤ簬鎸囧畾鐨勫疄渚嬶紝鎵�湁涓嶈兘mock鏋勯�鏂规硶鍜宻tatic鐨勬柟娉曪紝涔熶笉褰卞搷鍩虹被銆�/p>
绀轰緥锛�/p>
琚祴绫诲瀷锛�/p>
public final class ConcatenatingInputStream extends InputStream { private final Queue<InputStream> sequentialInputs; private InputStream currentInput; public ConcatenatingInputStream(InputStream... sequentialInputs) { this.sequentialInputs = new LinkedList<InputStream>(Arrays.asList(sequentialInputs)); currentInput = this.sequentialInputs.poll(); } @Override public int read() throws IOException { if (currentInput == null) return -1; int nextByte = currentInput.read(); if (nextByte >= 0) { return nextByte; } currentInput = sequentialInputs.poll(); return read(); } }
娴嬭瘯锛�/p>
@Test public void concatenateInputStreams( @Injectable final InputStream input1, @Injectable final InputStream input2) throws Exception { new Expectations() {{ input1.read(); returns(1, 2, -1); input2.read(); returns(3, -1); }}; InputStream concatenatedInput = new ConcatenatingInputStream(input1, input2); byte[] buf = new byte[3]; concatenatedInput.read(buf); assertArrayEquals(new byte[] {1, 2, 3}, buf); }
娉ㄦ剰杩欓噷蹇呴』浣跨敤@Injectable 鐨勫師鍥犳槸锛屾垜浠笉鎯冲奖鍝嶅熀绫�InputStream 鐨�read() 鏂规硶, 鎵�互涓嶈兘鐢�@Mocked銆�nbsp;
onInstance(m)
constraint鎴戜滑浣跨敤 @Mocked 鎴�@Capturing 鏃讹紝涔熷彲浠ュ湪record 闃舵閫氳繃 onInstance(mocked) 鏂规硶鏉ラ檺鍒朵綔鐢ㄨ寖鍥翠粎鍦ㄦ寚瀹氱殑瀹炰緥涓娿�
@Test public void matchOnMockInstance(@Mocked final Collaborator mock) { new Expectations() {{ onInstance(mock).getValue(); result = 12; }}; // Exercise code under test with mocked instance passed from the test: int result = mock.getValue(); assertEquals(12, result); // If another instance is created inside code under test... Collaborator another = new Collaborator(); // ...we won't get the recorded result, but the default one: assertEquals(0, another.getValue()); }
瀹為檯涓婏紝濡傛灉 @Mocked 鎴�@Capturing 浣滅敤浜庣浉鍚岀被鍨嬬殑澶氫釜鍙橀噺鏃讹紝JMockit 浼氳嚜鍔ㄦ帹鏂�record 鏄粎浠�match 鎸囧畾鐨勫疄渚嬬殑銆備篃灏辨槸璇达紝涓嶉渶瑕�onInstance() 鏂规硶銆�/p>
涓嶅悓鐨勬瀯閫犳柟娉曚骇鐢熺殑瀹炰緥锛宺ecord 涓嶅悓鐨勮涓猴紝鍙互閲囩敤浠ヤ笅涓ょ鏂瑰紡涔嬩竴锛�/p>
鏂瑰紡涓�細
@Test public void newCollaboratorsWithDifferentBehaviors(@Mocked Collaborator anyCollaborator) { // Record different behaviors for each set of instances: new Expectations() {{ // One set, instances created with "a value": Collaborator col1 = new Collaborator("a value"); col1.doSomething(anyInt); result = 123; // Another set, instances created with "another value": Collaborator col2 = new Collaborator("another value"); col2.doSomething(anyInt); result = new InvalidStateException(); }}; // Code under test: new Collaborator("a value").doSomething(5); // will return 123 ... new Collaborator("another value").doSomething(0); // will throw the exception ... }
娉ㄦ剰杩欓噷锛宎nyCollaborator 鍙傛暟骞舵病鏈夌敤鍒帮紝浣嗚繖閲�@Mocked 浣垮緱绫诲瀷 Collaborator 鎴愪负Mocked 绫诲瀷锛屾柟鍙鍏舵柟娉曞拰鏋勯�鏂规硶褰曞埗銆�nbsp;
鏂瑰紡浜岋細
@Test public void newCollaboratorsWithDifferentBehaviors( @Mocked final Collaborator col1, @Mocked final Collaborator col2) { new Expectations() {{ // Map separate sets of future instances to separate mock parameters: new Collaborator("a value"); result = col1; new Collaborator("another value"); result = col2; // Record different behaviors for each set of instances: col1.doSomething(anyInt); result = 123; col2.doSomething(anyInt); result = new InvalidStateException(); }}; // Code under test: new Collaborator("a value").doSomething(5); // will return 123 ... new Collaborator("another value").doSomething(0); // will throw the exception ... }
鍥犱负杩欓噷@Mocked 浣滅敤浜庝袱涓悓绫诲瀷鐨勫彉閲忥紝浠庤�鍐冲畾浜嗕箣鍚庡澶嶅彂鍜岀殑褰曞埗鏄叧鑱斿埌瀹炰緥鑰岄潪绫汇�
鍦ㄥ綍鍒舵垨鑰呴獙璇侀樁娈碉紝 涓嶅彲鑳界┓涓炬墍鏈夊彲鑳界殑鍙傛暟鍊硷紝杩欐椂灏遍渶瑕佷娇鐢ㄥ脊鎬у尮閰嶃�
寮规�鍖归厤閫氳繃涓ょ鏂瑰紡琛ㄨ揪锛宎nyXxx 鎴�withXx() 鏂规硶锛屼粬浠兘鏄�Expectations 鍜�Verifacations 鐨勫熀绫�mockit.Invocations 鐨勬垚鍛橈紝鎵�互瀵规墍鏈夌殑Expectation 鍜�Verifaction 鍙敤銆�/p>
涓夌被锛氬師濮嬪強鍏跺寘瑁呯被鍨嬶紝anyString , 杩樻湁涓�釜 any 銆�nbsp;
@Test public void someTestMethod(@Mocked final DependencyAbc abc) { final DataItem item = new DataItem(...); new Expectations() {{ // Will match "voidMethod(String, List)" invocations where the first argument is // any string and the second any list. abc.voidMethod(anyString, (List<?>) any); }}; new UnitUnderTest().doSomething(item); new Verifications() {{ // Matches invocations to the specified method with any value of type long or Long. abc.anotherVoidMethod(anyLong); }}; }
娉ㄦ剰杩欓噷鎬庢牱琛ㄨ揪 any list 鐨勩�
闄や簡棰勫畾涔夌殑 withXx() 锛�/p>
瀹炰緥锛�withSameInstance(), withEqual()/withNotEqual(), withNull()/withNotNull()
绫诲瀷锛�withAny(T ) withInstanceLike() withInstanceOf()
String 锛�withMatch(), withSubString(), withPrefix()/withSuffix()
杩樺彲浠ラ�杩�with(Delegate ) 鍜�withArgThat(org.hamcrest.Matcher )鑷畾涔夊尮閰嶃�
null
value to match any object reference鍙互浣跨敤 null 鍘绘浛浠�any 鎴�withAny() 鏉ラ厤缃换鎰忓�锛屼絾浠呴檺浜庤嚦灏戜竴涓弬鏁颁娇鐢ㄤ簡 anyXx 鎴�withXx() 锛屽惁鍒欏畠涓ユ牸鍖归厤 null 銆�鍙﹀鍙敤 withNull() 鍘诲尮閰�null 鍊笺�
涔熷幓鏄锛屽鏋滄湁鏌愪釜鍙傛暟鍖归厤閲囩敤寮规�琛ㄨ揪寮忥紝鍓╀笅鐨勫弬鏁板鏋滈渶瑕佸尮閰�null 蹇呴』浣跨敤 withNull() 銆�/p>
濡傛灉鏈夌‘瀹氱殑涓暟锛屾瘡涓殑鍊间篃纭畾锛屽彲浠ヤ竴涓�垪鍑恒� 濡傛灉涓暟鏃犳墍璋擄紙鍖呮嫭0锛夛紝鍙互((Object[]) any) 鐨勬柟寮忋� 濡傦紝(String[])any 銆�/p>
涓嶈兘缁勫悎浣跨敤涓ユ牸鍊煎尮閰嶅拰寮规�鍖归厤銆�/p>
鍦ㄥ綍鍒跺拰楠岃瘉闃舵閮藉彲浠ユ寚瀹氾紱鎸囧畾鍦ㄥ搴旂殑鏂规硶涔嬪悗銆�/p>
閫氳繃涓変釜灞炴�鎸囧畾锛�times, minTimes 鍜�maxTimes 銆�姣忎釜灞炴�鍙兘浣跨敤涓�銆�/p>
times=0 鎴�maxTimes=0 鎰忓懗鐫�竴鏃︾洰鏍囨柟娉曡璋冪敤娴嬭瘯灏眆ail銆�/p>
濡傚墠鏂囷紝瀵�mock鏂规硶璋冪敤鐨勬鏁颁篃鍙互鍦ㄩ獙璇侀樁娈垫寚瀹氾紝鍏惰娉曡鍒欏拰褰曞埗闃舵鏄竴鏍风殑銆�/p>
鍥犱负 StrictExpectations 宸茬粡涓ユ牸鐨勮〃杈句簡楠岃瘉鐨勮姹傦紝涓嶈兘澶氫篃涓嶈兘灏戯紝鎵�互涓嶈兘鍐嶆湁 Verifacations 鍧椼�
瀵逛笌 regular鐨�Expectations , 鍥犱负瀹�record 鐨勬槸鏈�綆瑕佹眰锛屽鏋滆繕鏈夋洿澶氳姹傚垯闇�鍦�Verifications 闃舵琛ュ厖銆傛瘮濡傦紝涓嶅厑璁告煇涓柟娉曡璋冪敤锛屽彲浠ラ�杩�times=0 鎸囧畾銆�nbsp;
濡傛灉闇�楠岃瘉鏂规硶 replay 鐨勯『搴忥紝鍒欎娇鐢�VerificationsInOrder 銆�nbsp;
@Test public void verifyingExpectationsInOrder(@Mocked final DependencyAbc abc) { // Somewhere inside the tested code: abc.aMethod(); abc.doSomething("blah", 123); abc.anotherMethod(5); ... new VerificationsInOrder() {{ // The order of these invocations must be the same as the order // of occurrence during replay of the matching invocations. abc.aMethod(); abc.anotherMethod(anyInt); }}; }
娉ㄦ剰杩欓噷锛�abc.doSomething() 娌℃湁楠岃瘉锛屾墍浠ュ畠鏄惁鍑虹幇鍙婂嚭鐜扮殑椤哄簭閮芥棤鍏崇揣瑕併�
鍙互閫氳繃 unverifiedInvocations() 闅旂鏂规硶涔嬮棿椤哄簭鐨勭浉鍏虫�銆�/p>
@Mocked DependencyAbc abc; @Mocked AnotherDependency xyz; @Test public void verifyingTheOrderOfSomeExpectationsRelativeToAllOthers() { new UnitUnderTest().doSomething(); new VerificationsInOrder() {{ abc.methodThatNeedsToExecuteFirst(); unverifiedInvocations(); // Invocations not verified must come here... xyz.method1(); abc.method2(); unverifiedInvocations(); // ... and/or here. xyz.methodThatNeedsToExecuteLast(); }}; }
杩欓噷浼氶獙璇�1锛宎bc.methodThtNeedsToExecuteFirst() 鏄涓�釜璋冪敤 2锛寈yz.methodThatNeedsToExecuteLast() 鏄渶鍚庝竴涓皟鐢紱 3锛寈yz.method1(); abc.method2() 渚濇璋冪敤锛涗絾鍦�unverifiedInvocations() 鍦版柟鍙兘杩樻湁鏇村鐨勬柟娉曡皟鐢紝浣嗘棤鎵�皳銆�nbsp;
鏈夋椂锛屾垜浠叧蹇冩煇鍑犱釜鏂规硶鐨勮皟鐢ㄩ『搴忥紝浣嗗墿涓嬬殑鍑犱釜鏂规硶鍙獙璇佽皟鐢ㄤ簡锛屼笉瑕佹眰椤哄簭锛岃繖鏃跺彲閫氳繃涓や釜楠岃瘉鍧楁潵琛ㄨ揪锛�/p>
1 @Test 2 public void verifyFirstAndLastCallsWithOthersInBetweenInAnyOrder() 3 { 4 // Invocations that occur while exercising the code under test: 5 mock.prepare(); 6 mock.setSomethingElse("anotherValue"); 7 mock.setSomething(123); 8 mock.notifyBeforeSave(); 9 mock.save(); 10 11 new VerificationsInOrder() {{ 12 mock.prepare(); // first expected call 13 unverifiedInvocations(); // others at this point 14 mock.notifyBeforeSave(); // just before last 15 mock.save(); times = 1; // last expected call 16 }}; 17 18 // Unordered verification of the invocations previously left unverified. 19 // Could be ordered, but then it would be simpler to just include these invocations 20 // in the previous block, at the place where "unverifiedInvocations()" is called. 21 new Verifications() {{ 22 mock.setSomething(123); 23 mock.setSomethingElse(anyString); 24 }}; 25 }
涓婇潰鍗充娇鍘绘帀绗�3琛岋紝娴嬭瘯涔熼�杩囥�浣嗗嵈涓嶈兘楠岃瘉 22,23 琛屽嚭鐜板湪13銆�澶氫釜楠岃瘉鍧楁椂锛屽畠浠殑鐩稿椤哄簭寰堥噸瑕併�
涓ユ牸楠岃瘉琚皟鐢ㄧ殑鏂规硶鏈夊摢浜涗釜锛屼笉鑳藉涔熶笉鑳藉皯锛屼絾椤哄簭鏃犲叧銆�/p>
1 @Test 2 public void verifyAllInvocations(@Mocked final Dependency mock) 3 { 4 // Code under test included here for easy reference: 5 mock.setSomething(123); 6 mock.setSomethingElse("anotherValue"); 7 mock.setSomething(45); 8 mock.save(); 9 10 new FullVerifications() {{ 11 // Verifications here are unordered, so the following invocations could be in any order. 12 mock.setSomething(anyInt); // verifies two actual invocations 13 mock.setSomethingElse(anyString); 14 mock.save(); // if this verification (or any other above) is removed the test will fail 15 }}; 16 }
娉ㄦ剰濡傛灉鏈�皬娆℃暟楠岃瘉鍦‥xpectations 涓寚瀹氾紝鍒欏湪 FullExpections 涓棤闇�啀鎸囧畾銆�杩欎釜楠岃瘉濮嬬粓鍦ㄦ祴璇曠粨鏉熷墠鎵ц銆�/p>
@Test public void verifyAllInvocationsInOrder(@Mocked final Dependency mock) { // Code under test included here for easy reference: mock.setSomething(123); mock.setSomethingElse("anotherValue"); mock.setSomething(45); mock.save(); new FullVerificationsInOrder() {{ mock.setSomething(anyInt); mock.setSomethingElse(anyString); mock.setSomething(anyInt); mock.save(); }}; }
杩欓噷锛�mock.setSomething(anyInt) 蹇呴』鎸囧畾涓ゆ銆�nbsp;
@Test public void verifyAllInvocationsToOnlyOneOfTwoMockedTypes( @Mocked final Dependency mock1, @Mocked AnotherDependency mock2) { // Inside code under test: mock1.prepare(); mock1.setSomething(123); mock2.doSomething(); mock1.editABunchMoreStuff(); mock1.save(); new FullVerifications(mock1) {{ mock1.prepare(); mock1.setSomething(anyInt); mock1.editABunchMoreStuff(); mock1.save(); times = 1; }}; }
涔熷氨鏄紶閫�mock objects 鎴�Classes 鍋氬弬鏁扮粰 FullVerifications(...) , 鍗冲彧鍏冲績鎸囧畾鐨�mock 瀵硅薄鎴栫被鍨嬨�
鍙渶瑕佷娇鐢ㄧ┖鐨�FullVerifications 鍧楀嵆鍙� new FullInvocations(){{}};
褰曞埗鏃跺叿鏈夐殣鍚殑楠岃瘉鍔熻兘锛屽浜庝笅渚�/p>
new Expectations() {{ mock.a(); time=1; }} .... new FullVerifications(){{ }};
Expectations 鍧楄姹俶ock.a()鍙兘琚皟鐢ㄤ竴娆★紝浣�FullVerifications 鍧楀垯杩涜�瑕佹眰娌℃湁鍏朵粬鐨勬柟娉曡皟鐢ㄣ�
濡傛灉澶氫釜 mock 绫诲瀷鎴栧疄渚嬶紝鑰屾垜浠苟涓嶅叧蹇冩墍鏈夌殑锛岄偅鍙渶鍦�FullVerifications 鐨勫弬鏁板垪鍑烘劅鍏磋叮鐨勭被鎴栧疄渚嬪氨琛屼簡銆�nbsp;
Test public void verifyNoInvocationsOnOneOfTwoMockedDependenciesBeyondThoseRecordedAsExpected( @Mocked final Dependency mock1, @Mocked final AnotherDependency mock2) { new Expectations() {{ // These two are recorded as expected: mock1.setSomething(anyInt); mock2.doSomething(); times = 1; }}; // Inside code under test: mock1.prepare(); mock1.setSomething(1); mock1.setSomething(2); mock1.save(); mock2.doSomething(); // Will verify that no invocations other than to "doSomething()" occurred on mock2: new FullVerifications(mock2) {}; }
杩欓噷琛ㄨ揪浜嗭紝闄や簡 doSomething() 闇�琚皟鐢ㄤ竴娆′箣澶栵紝mock2 涓嶈兘鏈夊叾浠栨柟娉曡皟鐢ㄣ�
涓婇潰宸茬粡鎻愬埌锛屽鏋滆淇濊瘉鏌愪簺鏂规硶涓嶈璋冪敤鐨勮瘽锛屼笉蹇呭湪 Expectations / verification 鍧椾腑涓�竴鎸囨槑 time=0 銆�鍙涓嶅湪 FullVerification 鍧椾腑鎻愬埌閭d簺鏂规硶灏辫浜嗭細
@Test public void readOnlyOperation(@Mocked final Dependency mock) { new Expectations() {{ mock.getData(); result = "test data"; }}; // Code under test: String data = mock.getData(); // mock.save() should not be called here ... new FullVerifications() {{ mock.getData(); minTimes = 0; // calls to getData() are allowed, others are not }}; }
瀵瑰弬鏁伴獙璇佸線寰�槸閫氳繃涔嬪墠鎻愬埌鐨勫弬鏁板尮閰嶆潵瀹炵幇鐨勶紝杩欓噷鎻愬埌浜嗗彟澶栦竴绉嶆洿鐩磋鐨勬柟寮忥紝鎶婂弬鏁版崟鎹夊埌鐒跺悗鍋氳繘涓�鐨勯獙璇併�
杩欐椂瑕佷娇鐢�withCapture(...) 浣滀负鍙傛暟鍖归厤鍣�鎰忓懗鐫�紝姝ゆ椂鍙幏寰楀叾鍊硷紝涓嶅仛鍖归厤)銆�鏈変笁绉嶄娇鐢ㄦ儏鍐碉細
鎹曟崏鍙皟鐢ㄤ竴娆$殑鏂规硶鐨勫弬鏁帮細
@Test public void capturingArgumentsFromSingleInvocation(@Mocked final Collaborator mock) { // Inside tested code: new Collaborator().doSomething(0.5, new int[2], "test"); new Verifications() {{ double d; String s; mock.doSomething(d = withCapture(), null, s = withCapture()); assertTrue(d > 0.0); assertTrue(s.length() > 1); }}; }
娉ㄦ剰锛岃繖閲岀殑 withCapture() 鍙兘鐢ㄥ湪Verifications 鍧椾腑銆�濡傛灉杩欎釜鏂规硶琚皟鐢ㄥ娆★紝閭f嬁鍒扮殑鏄渶鍚庝竴娆$殑鍊笺�
鎹曟崏璋冪敤澶氭鐨勬柟娉曠殑鍙傛暟
@Test public void capturingArgumentsFromMultipleInvocations(@Mocked final Collaborator mock) { mock.doSomething(dataObject1); mock.doSomething(dataObject2); new Verifications() {{ List<DataObject> dataObjects = new ArrayList<>(); mock.doSomething(withCapture(dataObjects)); assertEquals(2, dataObjects.size()); DataObject data1 = dataObjects.get(0); DataObject data2 = dataObjects.get(1); // Perform arbitrary assertions on data1 and data2. }}; }
鍜屽墠闈笉鍚岋紝杩欓噷鐨�withCaptrue(List<T>) 涔熷彲浠ョ敤鍦�Expectation 鍧椾腑銆�nbsp;
鎹曟崏鍦ㄥ洖鏀捐繃绋嬩腑鍒涘缓鐨�mock 瀹炰緥锛�/strong>
1 @Test 2 public void capturingNewInstances(@Mocked Person mockedPerson) 3 { 4 // From the code under test: 5 dao.create(new Person("Paul", 10)); 6 dao.create(new Person("Mary", 15)); 7 dao.create(new Person("Joe", 20)); 8 9 new Verifications() {{ 10 // Captures the new instances created with a specific constructor. 11 List<Person> personsInstantiated = withCapture(new Person(anyString, anyInt)); 12 13 // Now captures the instances of the same type passed to a method. 14 List<Person> personsCreated = new ArrayList<>(); 15 dao.create(withCapture(personsCreated)); 16 17 // Finally, verifies both lists are the same. 18 assertEquals(personsInstantiated, personsCreated); 19 }}; 20 }
鎴戜滑涔嬪墠宸茬粡鐪嬪埌濡備綍閫氳繃 result 灞炴�鎴�returns(...) 褰曞埗缁撴灉銆�涔熺湅鍒颁簡濡備綍閫氳繃 anyXxx 灞炴�鎴�withXxx() 鏂规硶鏉ュ尮閰嶆柟娉曞弬鏁般� 閭e浣曟牴鎹洖鏀炬椂浼犲叆鐨勫弬鏁拌繑鍥炲搷搴旂殑缁撴灉鍛紝鍙互閫氳繃 Delegate 鎺ュ彛瀹氬埗锛�/p>
@Test public void delegatingInvocationsToACustomDelegate(@Mocked final DependencyAbc anyAbc) { new Expectations() {{ anyAbc.intReturningMethod(anyInt, null); result = new Delegate() { int aDelegateMethod(int i, String s) { return i == 1 ? i : s.length(); } }; }}; // Calls to "intReturningMethod(int, String)" will execute the delegate method above. new UnitUnderTest().doSomething(); }
姝ゆ帴鍙f槸涓爣蹇楁帴鍙o紝娌℃湁鏂规硶銆傛墍浠ユ柟娉曞悕鍙互闅忔剰锛�鍙傛暟鏈変袱绉嶉�鎷╋紝瑕佷箞鍜宺ecord鐨勬柟娉曞弬鏁颁竴鑷达紝瑕佷箞娌℃湁鍙傛暟锛岃繖涓ょ閫夋嫨閮藉厑璁告坊鍔犱竴涓�Invocation 绫诲瀷鍋氫负绗竴涓弬鏁般�
閫氳繃 Delegate 涔熷彲浠ュ畾鍒舵瀯閫犳柟娉曪細
@Test public void delegatingConstructorInvocations(@Mocked Collaborator anyCollaboratorInstance) { new Expectations() {{ new Collaborator(anyInt); result = new Delegate() { void delegate(int i) { if (i < 1) throw new IllegalArgumentException(); } }; }}; // The first instantiation using "Collaborator(int)" will execute the delegate above. new Collaborator(4); }
杩欑鎯呭喌锛�obj1.getObj2(..).getObj3().getObj4().doSomething(...) , 闇�mock鎵�湁杩欓摼鎺ョ殑鍑犱釜瀵硅薄鍜岀被銆�nbsp;
@Test public void recordAndVerifyExpectationsOnCascadedMocks( @Mocked Socket anySocket, // will match any new Socket object created during the test @Mocked final SocketChannel cascadedChannel // will match cascaded instances ) throws Exception { new Expectations() {{ // Calls to Socket#getChannel() will automatically return a cascaded SocketChannel; // such an instance will be the same as the second mock parameter, allowing us to // use it for expectations that will match all cascaded channel instances: cascadedChannel.isConnected(); result = false; }}; // Inside production code: Socket sk = new Socket(); // mocked as "anySocket" SocketChannel ch = sk.getChannel(); // mocked as "cascadedChannel" if (!ch.isConnected()) { SocketAddress sa = new InetSocketAddress("remoteHost", 123); ch.connect(sa); } InetAddress adr1 = sk.getInetAddress(); // returns a newly created InetAddress instance InetAddress adr2 = sk.getLocalAddress(); // returns another new instance ... // Back in test code: new Verifications() {{ cascadedChannel.connect((SocketAddress) withNotNull()); }}; }
鍥犱负Socket, SocketChannel 閮借 @Mocked, 灏界娌℃湁褰曞埗 Socket#getChannel(), 姝ゆ柟娉曟瘡娆¤皟鐢ㄤ粛鐒朵細杩斿洖鐩稿悓鐨�mocked SockecChannel 瀹炰緥銆�nbsp;濡傛灉涓嶅瓨鍦ㄤ竴涓猰ocked 瀹炰緥绫诲瀷涓庤繑鍥炲�鍖归厤锛岄偅瀹冩瘡娆¢兘杩斿洖涓嶅悓鐨勭殑瀹炰緥銆�/span>
娉ㄦ剰锛屾柟娉曡嫢娌℃湁琚綍鍒讹紝缂虹渷杩斿洖绌哄璞� 鎵�湁鏂规硶閮芥槸绌哄疄鐜扮殑瀵硅薄, 涓嶆槸 null 銆�浣�String - null, Object - null, 闆嗗悎-闀垮害涓�鐨勯泦鍚堛�
杩欑绾ц仈mock鐨勭壒鎬у湪mocked绫诲瀷鍖呭惈闈欐�鐨勫伐鍘傛柟娉曟槸灏や负鏈夌敤锛�/span>
public void postErrorMessageToUIForInvalidInputFields(@Mocked final FacesContext jsf) { // Set up invalid inputs, somehow. // Code under test which validates input fields from a JSF page, adding // error messages to the JSF context in case of validation failures. FacesContext ctx = FacesContext.getCurrentInstance(); if (some input is invalid) { ctx.addMessage(null, new FacesMessage("Input xyz is invalid: blah blah...")); } ... // Test code: verify appropriate error message was added to context. new Verifications() {{ FacesMessage msg; jsf.addMessage(null, msg = withCapture()); assertTrue(msg.getSummary().contains("blah blah")); }}; }
浣犵湅锛屾垜浠棤闇�綍鍒�FacesContext.getCurrentInstance()锛屽畠灏卞彲鐢ㄣ�
娴佸紡(Fluent )鎺ュ彛鏄彟涓�釜绾ц仈mock濂界敤鐨勬儏鍐碉細
@Test public void createOSProcessToCopyTempFiles(@Mocked final ProcessBuilder pb) throws Exception { // Code under test creates a new process to execute an OS-specific command. String cmdLine = "copy /Y *.txt D:\\TEMP"; File wrkDir = new File("C:\\TEMP"); Process copy = new ProcessBuilder().command(cmdLine).directory(wrkDir).inheritIO().start(); int exit = copy.waitFor(); ... // Verify the desired process was created with the correct command. new Verifications() {{ pb.command(withSubstring("copy")).start(); }}; }
杩欓噷 command(),directory() 鍜�inheritIO() 閮借繑鍥炲悓涓�釜pb 瀵硅薄锛�鏈�悗start()杩斿洖涓�釜鏂�mocked Process 瀵硅薄銆�/p>
鏈夋椂鎴戜滑闇�璁块棶琚祴瀵硅薄鍜宮ocked瀵硅薄鐨勭鏈夋垚鍛橈紝Deencapsulation 鎻愪緵浜嗛潤鎬佹柟娉曟潵瑙e喅杩欎釜闂锛�/p>
import static mockit.Deencapsulation.*; @Test public void someTestMethod(@Mocked final DependencyAbc abc) { final CodeUnderTest tested = new CodeUnderTest(); // Defines some necessary state on the tested object: setField(tested, "someIntField", 123); new Expectations() {{ // Expectations still recorded, even if the invocations are done through Reflection: newInstance("some.package.AnotherDependency", true, "test"); maxTimes = 1; invoke(abc, "intReturningMethod", 45, ""); result = 1; // other expectations recorded... }}; tested.doSomething(); String result = getField(tested, "result"); assertEquals("expected result", result); }
杩欓噷鐢ㄥ埌浜嗗洓涓柟娉曪細setField()/getField(), newInstance() 鍜�invoke();
娉ㄦ剰鎴戜滑涓嶄粎浠呴�杩囧弽灏勮闂娴嬬被鐨勭鏈夋垚鍛橈紝瀵筸ocked 绫�瀹炰緥鐨勭鏈夋柟娉曠殑褰曞埗鍜岄獙璇侊紝閫氳繃鍙嶅皠璋冪敤涔熸槸鍙互鐨勩� 褰撶劧锛岄�甯镐笉闇�褰曞埗鍜岄獙璇佺鏈夋柟娉曪紝闄ら潪鍦ㄥ彧mock閮ㄥ垎鏂规硶鐨勫満鏅笅銆�/p>
缂虹渷鐨勶紝@Mocked 鏍囨敞鐨勭被鍨嬪強鍏剁埗绫诲瀷(闄や簡Object)鐨勬墍鏈夋柟娉曞拰鏋勯�鏂规硶閮借mock銆俶ock閮ㄥ垎鏂规硶灏辨槸鍙湁褰曞埗杩囩殑鏂规硶鎵嶈mock, 鑰屽叾浠栫殑鏂规硶鐩存帴璋冪敤瀹為檯鐨勫疄鐜般�
public class PartialMockingTest { static class Collaborator { final int value; Collaborator() { value = -1; } Collaborator(int value) { this.value = value; } int getValue() { return value; } final boolean simpleOperation(int a, String b, Date c) { return true; } static void doSomething(boolean b, String s) { throw new IllegalStateException(); } } @Test public void partiallyMockingAClassAndItsInstances() { final Collaborator anyInstance = new Collaborator(); new Expectations(Collaborator.class) {{ anyInstance.getValue(); result = 123; }}; // Not mocked, as no constructor expectations were recorded: Collaborator c1 = new Collaborator(); Collaborator c2 = new Collaborator(150); // Mocked, as a matching method expectation was recorded: assertEquals(123, c1.getValue()); assertEquals(123, c2.getValue()); // Not mocked: assertTrue(c1.simpleOperation(1, "b", null)); assertEquals(45, new Collaborator(45).value); } @Test public void partiallyMockingASingleInstance() { final Collaborator collaborator = new Collaborator(2); new Expectations(collaborator) {{ collaborator.getValue(); result = 123; collaborator.simpleOperation(1, "", null); result = false; // Static methods can be dynamically mocked too. Collaborator.doSomething(anyBoolean, "test"); }}; // Mocked: assertEquals(123, collaborator.getValue()); assertFalse(collaborator.simpleOperation(1, "", null)); Collaborator.doSomething(true, "test"); // Not mocked: assertEquals(2, collaborator.value); assertEquals(45, new Collaborator(45).getValue()); assertEquals(-1, new Collaborator().getValue()); } }
mock 閮ㄥ垎鏂规硶鏃讹紝闇�鎸囧畾褰曞埗鐨勬柟娉曞奖鍝嶆煇涓疄渚嬭繕鏄被銆傚彇鍐充簬浼犵粰鏋勯�鏂规硶 Expectations(Object ...) 鐨勫弬鏁版槸绫昏繕鏄疄渚嬨� 濡傛灉鏄被锛屽垯绫诲強鍩虹被鐨勬墍鏈夋柟娉曞強鏋勯�鏂规硶閮藉彲浠ヨ褰曞埗銆�/span>
涓�釜瀵硅薄鍦ㄩ�杩噀xpectations 鏋勯�鏂规硶杞寲鎴恗ock瀵硅薄涔嬪悗锛屽畠涔嬪墠鐨勭姸鎬�field鍊�浠嶇劧淇濈暀銆�/p>
杩欓噷鎬荤粨涓� 鎶婄被鎴栧璞″彉鎴恗ocked 鏈変袱绉嶆柟寮忥細 鐢�@Mocked 鎴栧仛涓�Expectations()鐨勫弬鏁般�鍓嶈�鍗充娇涓嶅綍鍒讹紝鍏舵墍鏈夋柟娉曚篃鍚屾椂琚玬ock锛涘湪浣跨敤鍚庤�鏃讹紝灏界绫绘垨瀹炰緥杞寲鎴愪簡mocked, 浣嗗叾鏂规硶鍙湁鍦ㄥ綍鍒朵箣鍚庢柟娉曟墠鏄痬ocked銆�nbsp;
鍙鏄痬ocked 绫诲瀷鎴栧疄渚嬶紝鍏舵柟娉曞氨鍙互琚�verify 銆傚嵆浣胯繖涓柟娉曟病鏈塵ocked -- 娌℃湁褰曞埗銆�杩欓噷鍜�Mockito 鐨剆py瀵硅薄鐨勬蹇典竴鑷淬�
@Test public void partiallyMockingAnObjectJustForVerifications() { final Collaborator collaborator = new Collaborator(123); new Expectations(collaborator) {}; // No expectations were recorded, so nothing will be mocked. int value = collaborator.getValue(); // value == 123 collaborator.simpleOperation(45, "testing", new Date()); ... // Unmocked methods can still be verified: new Verifications() {{ c1.simpleOperation(anyInt, anyString, (Date) any); }}; }
鏈�悗琛ュ厖涓�偣锛屽簲鐢ㄩ儴鍒唌ocking 鏇寸畝鍗曠殑鏂瑰紡鏄妸娴嬭瘯绫荤殑鎴愬憳鍙橀噺涓�苟鏍囨敞@Mocked 鍜孈Tested锛�杩欐牱锛屾鍙橀噺涓嶄紶缁橢xpectations 鐨勬瀯閫犳柟娉曘�浣嗘垜浠换鐒堕渶瑕佸綍鍒堕渶瑕佺殑鏂规硶銆侤Tested 鎰忓懗鐫�娴嬪璞★紝涓よ�缁勫悎灏辨槸鎶妋ocked 瀹炰緥/绫讳篃褰撳仛琚祴绫伙紝浠庤�鍙互娴嬭瘯瀹冩病鏈夎mock鐨勬柟娉曘�
涓嬮潰杩欎釜渚嬪瓙骞朵笉瀹炵敤锛�浣嗚兘鏄庝簡鍦拌鏄庨棶棰樸�鐪嬪彂琛岀増闅忛檮鐨勭ず渚嬪垯鏇村疄鐢紝鍙弬鑰冨叾涓�nbsp;Timing Framework 銆�/p>
public interface Service { int doSomething(); } final class ServiceImpl implements Service { public int doSomething() { return 1; } } public final class TestedUnit { private final Service service1 = new ServiceImpl(); private final Service service2 = new Service() { public int doSomething() { return 2; } }; public int businessOperation() { return service1.doSomething() + service2.doSomething(); } }
businessOperation() 鏄垜浠娴嬭瘯鐨勭洰鏍囨柟娉曘�
鐩墠鐨勯棶棰樻槸锛宻ervice1 鍜�service2 鏄�final 鐨勫睘鎬э紝涓嶈兘閫氳繃鍙嶅皠璁剧疆銆傛垜浠渶瑕佸湪瀹炰緥鍖栬娴嬪疄渚嬪墠鍏�nbsp;mock ServiceImple 鍜岄偅涓尶鍚嶇被鐨�doSomething(), 浣嗘槸鍖垮悕绫伙紝鎬庝箞mock鍛紒
@Capaturing 鍙互甯垜浠疄鐜拌繖涓洰鏍囷細 缁欏畾鍩虹被鎴栨帴鍙o紝浣垮叾鎵�湁瀛愮被琚�mock銆�nbsp;
public final class UnitTest { @Capturing Service anyService; @Test public void mockingImplementationClassesFromAGivenBaseType() { new Expectations() {{ anyService.doSomething(); returns(3, 4); }}; int result = new TestedUnit().businessOperation(); assertEquals(7, result); } }
涓婁緥锛宺ecord 闃舵涓や釜杩斿洖鍊兼寚瀹氱粰 Service#doSomething(), 鍦╮eplay闃舵锛屼笉绠″疄鐜扮被鎴栧疄渚嬫槸鍝釜锛屽彧瑕佽皟鐢ㄨ繖涓柟娉曢兘浼氬彈鍒板奖鍝嶃�
@Capturing 鏈変釜int绫诲瀷鍙�灞炴� "maxInstances" 鍙互闄愬埗鍙楀奖鍝嶇殑瀹炰緥涓暟锛涜繖鏍锋垜浠氨鍙互涓哄悓涓�熀绫荤殑澶氫釜mock瀹炰緥 record 鎴�verify 涓嶅悓鐨勮涓恒�杩欏彲浠ラ�杩囧畾涔�鍒板涓狜Capturing fileds/parameters, 姣忎釜閮芥寚瀹�maxInstance , 褰撶劧鏈�悗涓�釜鍙互娌℃湁銆�/p>
涓嬮潰瀹炰緥浠呬粎涓轰簡鏂逛究鐞嗚В锛屽苟鏃犲疄闄呯敤閫旓細
@Test public void testWithDifferentBehaviorForFirstNewInstanceAndRemainingNewInstances( @Capturing(maxInstances = 1) final Buffer firstNewBuffer, @Capturing final Buffer remainingNewBuffers) { new Expectations() {{ firstNewBuffer.position(); result = 10; remainingNewBuffers.position(); result = 20; }}; // Code under test creates several buffers... ByteBuffer buffer1 = ByteBuffer.allocate(100); IntBuffer buffer2 = IntBuffer.wrap(new int[] {1, 2, 3}); CharBuffer buffer3 = CharBuffer.wrap(" "); // ... and eventually read their positions, getting 10 for // the first buffer created, and 20 for the remaining ones. assertEquals(10, buffer1.position()); assertEquals(20, buffer2.position()); assertEquals(20, buffer3.position()); }
闇�娉ㄦ剰锛屽彧瑕佹湁 @Capturing 淇グ锛屾墍鏈夊疄渚嬮兘鏄痬ocked, 灏界瀹冨彲鑳芥病鏈夎褰曞埗銆�
寰�線锛屼竴涓祴璇曠被寰�線鎿嶄綔涓�釜琚祴鐨勭被銆侸Mockit 鎻愪緵鐨凘Tested 鍙互鑷姩瀹炰緥鍖栬繖涓被骞舵敞鍏ocked渚濊禆銆�/p>
@Tested 鐨勫睘鎬т笉鑳芥槸final鐨勶紝鍏跺疄渚嬪寲鍜屼緷璧栨敞鍏ュ彂鐢熷湪娴嬭瘯鏂规硶鎵ц鍓嶏紝姝ゆ椂鑻ュ睘鎬ф槸null,鍒欏疄渚嬪寲鍜屼緷璧栨敞鍏ュ氨浼氭墽琛岋紝鍚﹀垯鐣ヨ繃銆�/p>
渚濊禆娉ㄥ叆鐨刴ock闇�娴嬭瘯绫婚�杩嘆Injectable 鐨勫睘鎬ф垨鍙傛暟鎻愪緵锛孈Mocked 鎴�@Capturing 娌℃湁杩欎釜鍔熻兘銆傚彟澶栵紝闈瀖ock鐨勬暟鎹篃鍙互娉ㄥ叆锛屽鍘熷绫诲瀷鎴栨暟缁勩�
public class SomeTest { @Tested CodeUnderTest tested; @Injectable Dependency dep1; @Injectable AnotherDependency dep2; @Injectable int someIntegralProperty = 123; @Test public void someTestMethod(@Injectable("true") boolean flag, @Injectable("Mary") String name) { // Record expectations on mocked types, if needed. tested.exerciseCodeUnderTest(); // Verify expectations on mocked types, if required. } }
娉ㄦ剰锛孈Injectable 鐨勬暟鎹鏄潪mock鐨勶紝搴旇琚樉寮忕殑璧嬪�銆傚湪@Injectable 鏍囨敞鍙傛暟鏃讹紝璧嬪�鍙互閫氳繃 @Injectable 鐨剉alue 灞炴�銆�/p>
鏀寔鏋勯�鏂规硶娉ㄥ叆鍜屽瓧娈垫敞鍏ャ�瀵逛簬鍓嶈�锛岃兘瑕佽兘琚�@Injectable 鐨勫彉閲忓尮閰嶄笂銆�闇�敞鎰忥紝瀵逛簬涓�釜缁欏畾鐨勬祴璇曟柟娉曪紝鍙敞鍏ョ殑鍙橀噺鏄疈Injectable fileds鍜岃繖涓祴璇曟柟娉旲Injectable 鍙傛暟鐨勭粍鍚堛�鍥犳锛屼笉鍚岀殑娴嬭瘯鏂规硶鍙互鎻愪緵涓嶅悓鐨勮娉ㄥ叆鐨勫弬鏁扮粍鍚堛�
鍦ㄨ娴嬭瘯鐨勭被鍨嬮�杩囪閫夋嫨鐨勬瀯閫犲櫒鍒濆鍖栦箣鍚庯紝灏卞紑濮嬫敞鍏ュ畠鐨勯潪final鐨勫睘鎬с�瀵逛簬姣忎釜瑕佹敞鍏ョ殑灞炴�鏉ヨ锛屽畠浼氱洿鎺ュ鍒舵祴璇曠被涓浉鍚岀被鍨嬬殑@Injectable 鍙橀噺锛屽鏋滅浉鍚岀被鍨嬬殑鏈夊涓紝灏辨牴鎹悕绉板尮閰嶃�
鏈�畝鍗曠殑鍔炴硶灏辨槸鎶妋ock瀵硅薄瀹氫箟鎴愬睘鎬с�
public final class LoginServiceTest { @Tested LoginService service; @Mocked UserAccount account; @Before public void init() { new NonStrictExpectations() {{ UserAccount.find("john"); result = account; }}; } @Test public void setAccountToLoggedInWhenPasswordMatches() throws Exception { willMatchPassword(true); service.login("john", "password"); new Verifications() {{ account.setLoggedIn(true); }}; } private void willMatchPassword(final boolean match) { new Expectations() {{ account.passwordMatches(anyString); result = match; }}; } @Test public void notSetAccountLoggedInIfPasswordDoesNotMatch() throws Exception { willMatchPassword(false); service.login("john", "password"); new Verifications() {{ account.setLoggedIn(true); times = 0; }}; } // other tests that use the "account" mock field }
涓や釜鏂规硶閮借娴嬭瘯 LoginService#login(String accountId, String password)
method銆傚畠灏濊瘯鍘婚�杩�accountId"鏌ヨ寰楀埌鐢ㄦ埛璐︽埛锛屽洜涓轰袱涓祴璇曢兘渚濊禆杩欎釜鏂规硶锛屾墍浠ユ彁杩囦竴涓�NonStrictExpectation 褰曞埗瀹冦�璁颁綇锛屼竴涓祴璇曞彲浠ユ湁澶氫釜 expectations/verifications 鍧楋紝褰撴牱鐨勫潡涔熷彲浠ュ湪鍏变韩鐨�"before", "after" 鏂规硶鍐呫�杩欓噷杩樻樉绀轰簡鎶婁竴涓叕鍏辩殑 expectations 鎶芥垚涓�釜鏂规硶锛屼互澶囩粰鎵�湁鐨勬祴璇曟柟娉曞叡浜�
鎴戜滑杩樺彲浠ュ垱寤篍xpectations 鐨勫瓙绫绘潵瀹屾垚鐩稿悓鐨勯噸鐢細
final class PasswordMatchingExpectations extends Expectations { PasswordMatchingExpectations(boolean match) { account.passwordMatches(anyString); result = match; } } @Test public void setAccountToLoggedInWhenPasswordMatches() throws Exception { new PasswordMatchingExpectations(true); ... }
鑷畾涔夌殑鎵╁睍绫婚�甯稿簲鏄�fianl 鐨勶紝闄ら潪鎵撶畻鍐嶅仛鎵╁睍锛屽湪閭g鎯呭喌涓嬶紝绫诲悕绉板繀椤讳互 "Expectations" 鎴�"Verifications" 缁撳熬锛屽惁鍒�JMockit 鏃犳硶璇嗗埆銆�/p>
涓嬮潰绀轰緥涓�釜mocked 瀵硅薄鎬庢牱瀹炵幇澶氫釜鎺ュ彛锛�/p>
public interface Dependency { String doSomething(boolean b); } public class MultiMocksTest<MultiMock extends Dependency & Runnable> { @Mocked MultiMock multiMock; @Test public void mockFieldWithTwoInterfaces() { new Expectations() {{ multiMock.doSomething(false); result = "test"; }}; multiMock.run(); assertEquals("test", multiMock.doSomething(false)); new Verifications() {{ multiMock.run(); }}; } @Test public <M extends Dependency & Serializable> void mockParameterWithTwoInterfaces( @Mocked final M mock) { new Expectations() {{ mock.doSomething(true); result = "abc"; }}; assertEquals("abc", mock.doSomething(true)); assertTrue(mock instanceof Serializable); } }
鏍稿績鏄繖鏍风殑璇硶 <MM extends A & B> 銆� 鐒跺悗鍒╃敤 MM 澹版槑mock 鐨勭被鍨嬨�
浣跨敤 strict expectations 褰曞埗鐨勮繛缁殑璋冪敤杩囩▼缂虹渷瑕佹眰鐨勬墽琛屾鏁版槸1锛屾垜浠篃鍙互瑕佹眰杩唬娆℃暟锛岄�杩�nbsp;StrictExpectations(numberOfIterations)
@Test public void recordStrictInvocationsInIteratingBlock(@Mocked final Collaborator mock) { new StrictExpectations(2) {{ mock.setSomething(anyInt); mock.save(); }}; // In the tested code: mock.setSomething(123); mock.save(); mock.setSomething(45); mock.save(); }
Expectations 鍜�NonstrictExpectations 涔熷彲浠ヤ娇鐢ㄨ繖绉嶆柟寮忋�杩唬娆℃暟浠呬粎浣滀负涓�釜鏈�ぇ/鏈�皬娆℃暟闄愬埗鐨勪箻鏁般�瀵逛簬 NonstrictExpectations 鏉ヨ
鍚屾牱鐨�Verifications 鏋勯�鏂规硶涔熷彲浠ユ寚瀹氳凯浠f鏁�/p>
@Test public void verifyAllInvocationsInLoop(@Mocked final Dependency mock) { int numberOfIterations = 3; // Code under test included here for easy reference: for (int i = 0; i < numberOfIterations; i++) { DataItem data = getData(i); mock.setData(data); mock.save(); } new Verifications(numberOfIterations) {{ mock.setData((DataItem) withNotNull()); mock.save(); }}; new VerificationsInOrder(numberOfIterations) {{ mock.setData((DataItem) withNotNull()); mock.save(); }}; }
渚嬩腑浣跨敤浜嗕袱绉�verifications锛屼粎浠呮槸涓轰簡璇存槑涓よ�鐨勫尯鍒�瀵逛簬鍓嶈�锛岃繖涓鏁板弬鏁颁粎浠呬綔涓轰竴涓箻鏁帮紝瀹冧笌鏄惧紡鐨勬鏁�times,minTimes,maxTimes)鐨勭Н鏋勬垚鏄疄闄呯殑娆℃暟瑕佹眰銆傝�瀵逛簬 VerificationsInOrder, 鍥犱负鏄惧紡鐨勬鏁拌缃瀹冩棤鏁� 鎵�互杩欎釜鍙傛暟鐩稿綋浜庢妸杩欎釜椤哄簭鐨勮皟鐢ㄨ繃绋嬮噸澶峮娆�銆�
鍚屾牱锛岃繖涓鏁板弬鏁板湪 FullVerifications 鍙�FullVerificationsInOrder 涓婂簲鐢ㄥ彲浠ョ被姣旇繖涓緥瀛愩�