JMockit 鎸囧崡 缈昏瘧

鍘熸枃 

 鏇存柊鏃堕棿锛�016-01-17 19:25:40

Mocked types and instances   mocked 鐨勭被鍜屽疄渚�/h2>

鏂规硶鍖呮嫭鏋勯�鏂规硶鏄�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>

  1. @Mocked , mock 绫诲強鍏跺熀绫汇�
  2. @Injectable , mock 绫荤殑鏌愪釜瀹炰緥锛岀被鐨勫叾瀹冨疄渚嬩笉鍙楀奖鍝嶃�
  3. @Capturing , mock 绫诲強鍏惰鐢熺被銆�/li>

娉ㄦ剰杩欓噷鎻愬埌鐨勭被涔熷寘鎷帴鍙c� 

涓庡叾浠栫殑mock妗嗘灦涓嶅悓, @Mocked 鍜�@Capturing 缂虹渷mock鐨勬槸绫汇�杩欐剰鍛崇潃锛屼竴鏃︿綘杩欐牱" @Mocked  SomeClass  ins1" ,  鍗充娇浣犺皟鐢ㄦ瀯閫犳柟娉昻ew涓�釜瀵硅薄 ins2锛宨ns2涔熸槸mock瀵硅薄銆�/p>

 

Expectations

涓�釜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.
}

 

 

The record-replay-verify model   褰曞埗-閲嶆斁-楠岃瘉 妯″瀷

浠讳綍娴嬭瘯鍙互琚垝鍒嗗埌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>

  1. 褰曞埗 - 棰勬湡mock 瀵硅薄鍙兘琚皟鐢ㄧ殑鏂规硶锛�鍙婂叾搴旇繑鍥炵殑缁撴灉锛�/li>
  2. 鍥炴斁 - 琚祴瀵硅薄鎵ц锛岃皟鐢�mock 瀵硅薄鐨勬柟娉曪紱
  3. 楠岃瘉 - 妫�煡mock 瀵硅薄鏄惁鎸夌収棰勬湡琚皟鐢紱

褰撶劧锛孍xpectations 鍧楁垨 Verifacations 鍧楀彲浠ユ湁澶氫釜涔熷彲浠ユ病鏈夈�

 

Regular, strict, and non-strict expectations

Expectations : 鎸囧畾鐨勬柟娉曟垨娆℃暟蹇呴』婊¤冻锛屾病鎸囧畾鐨勯殢鎰忋�浣嗕笉鍏冲績椤哄簭銆�/p>

StrictExpectation : 鎰忓懗鐫�弗鏍煎尮閰嶃� replay 闃舵璋冪敤浜嗗摢鍑犱釜鏂规硶锛岃皟鐢ㄧ殑娆℃暟(娌℃湁鎸囧畾灏辨槸1)鍜岄『搴忥紝閮藉繀椤诲拰 record 闃舵涓�牱锛屽惁鍒欐祴璇昮ail (unexpected invocation)銆�/p>

NonStrictExpectations : 鏃犳墍璋擄紝涔熷氨鏄浉褰撲簬涓嶅仛楠岃瘉锛屽彧鏄敤鏉ユ寚瀹歳esult銆傚缓璁粎琚敤浜巗etup(@Before )闃舵銆�nbsp;

浠ヤ笂涓夎�鐨勫尯鍒剰鍛崇潃瀵逛簬 StrictExpectation 涓嶉渶瑕�Verifications 鍧楋紝鑰孨onStrictExpectations 闇�銆�馃槃缁堜簬鏄庣櫧瀹冧负鍟ュ彨 Expectations 浜嗮煒�/p>

Recording results for an expectation

鍦‥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();
}

 

Matching invocations to specific instances

@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;

The 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>

Instances created with a given constructor

涓嶅悓鐨勬瀯閫犳柟娉曚骇鐢熺殑瀹炰緥锛宺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 浣滅敤浜庝袱涓悓绫诲瀷鐨勫彉閲忥紝浠庤�鍐冲畾浜嗕箣鍚庡澶嶅彂鍜岀殑褰曞埗鏄叧鑱斿埌瀹炰緥鑰岄潪绫汇� 

 

Flexible matching of argument values  鍙傛暟鍊肩殑寮规�鍖归厤

鍦ㄥ綍鍒舵垨鑰呴獙璇侀樁娈碉紝 涓嶅彲鑳界┓涓炬墍鏈夊彲鑳界殑鍙傛暟鍊硷紝杩欐椂灏遍渶瑕佷娇鐢ㄥ脊鎬у尮閰嶃�

寮规�鍖归厤閫氳繃涓ょ鏂瑰紡琛ㄨ揪锛宎nyXxx 鎴�withXx() 鏂规硶锛屼粬浠兘鏄�Expectations 鍜�Verifacations 鐨勫熀绫�mockit.Invocations 鐨勬垚鍛橈紝鎵�互瀵规墍鏈夌殑Expectation 鍜�Verifaction 鍙敤銆�/p>

Using the "any" fields for argument matching

涓夌被锛氬師濮嬪強鍏跺寘瑁呯被鍨嬶紝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 鐨勩� 

Using the "with" methods for argument matching

闄や簡棰勫畾涔夌殑 withXx() 锛�/p>

瀹炰緥锛�withSameInstance(), withEqual()/withNotEqual(), withNull()/withNotNull() 

绫诲瀷锛�withAny(T )  withInstanceLike()  withInstanceOf()

String 锛�withMatch(), withSubString(), withPrefix()/withSuffix()

杩樺彲浠ラ�杩�with(Delegate ) 鍜�withArgThat(org.hamcrest.Matcher )鑷畾涔夊尮閰嶃� 

Using the null value to match any object reference

鍙互浣跨敤 null 鍘绘浛浠�any 鎴�withAny() 鏉ラ厤缃换鎰忓�锛屼絾浠呴檺浜庤嚦灏戜竴涓弬鏁颁娇鐢ㄤ簡 anyXx 鎴�withXx() 锛屽惁鍒欏畠涓ユ牸鍖归厤 null 銆�鍙﹀鍙敤 withNull() 鍘诲尮閰�null 鍊笺� 

涔熷幓鏄锛屽鏋滄湁鏌愪釜鍙傛暟鍖归厤閲囩敤寮规�琛ㄨ揪寮忥紝鍓╀笅鐨勫弬鏁板鏋滈渶瑕佸尮閰�null 蹇呴』浣跨敤 withNull() 銆�/p>

Matching values passed through a varargs parameter 鍖归厤鍙橀暱鍙傛暟

濡傛灉鏈夌‘瀹氱殑涓暟锛屾瘡涓殑鍊间篃纭畾锛屽彲浠ヤ竴涓�垪鍑恒� 濡傛灉涓暟鏃犳墍璋擄紙鍖呮嫭0锛夛紝鍙互((Object[]) any) 鐨勬柟寮忋� 濡傦紝(String[])any 銆�/p>

涓嶈兘缁勫悎浣跨敤涓ユ牸鍊煎尮閰嶅拰寮规�鍖归厤銆�/p>

Specifying invocation count constraints  鎸囧畾璋冪敤娆℃暟绾︽潫

鍦ㄥ綍鍒跺拰楠岃瘉闃舵閮藉彲浠ユ寚瀹氾紱鎸囧畾鍦ㄥ搴旂殑鏂规硶涔嬪悗銆�/p>

閫氳繃涓変釜灞炴�鎸囧畾锛�times, minTimes 鍜�maxTimes 銆�姣忎釜灞炴�鍙兘浣跨敤涓�銆�/p>

times=0 鎴�maxTimes=0 鎰忓懗鐫�竴鏃︾洰鏍囨柟娉曡璋冪敤娴嬭瘯灏眆ail銆�/p>

 

Explicit verification  鏄惧紡鐨勯獙璇�/h2>

濡傚墠鏂囷紝瀵�mock鏂规硶璋冪敤鐨勬鏁颁篃鍙互鍦ㄩ獙璇侀樁娈垫寚瀹氾紝鍏惰娉曡鍒欏拰褰曞埗闃舵鏄竴鏍风殑銆�/p>

鍥犱负 StrictExpectations 宸茬粡涓ユ牸鐨勮〃杈句簡楠岃瘉鐨勮姹傦紝涓嶈兘澶氫篃涓嶈兘灏戯紝鎵�互涓嶈兘鍐嶆湁 Verifacations 鍧椼� 

瀵逛笌 regular鐨�Expectations , 鍥犱负瀹�record 鐨勬槸鏈�綆瑕佹眰锛屽鏋滆繕鏈夋洿澶氳姹傚垯闇�鍦�Verifications 闃舵琛ュ厖銆傛瘮濡傦紝涓嶅厑璁告煇涓柟娉曡璋冪敤锛屽彲浠ラ�杩�times=0 鎸囧畾銆�nbsp;

Verification in order  椤哄簭楠岃瘉

濡傛灉闇�楠岃瘉鏂规硶 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() 娌℃湁楠岃瘉锛屾墍浠ュ畠鏄惁鍑虹幇鍙婂嚭鐜扮殑椤哄簭閮芥棤鍏崇揣瑕併� 

Partially ordered verification 灞�儴椤哄簭楠岃瘉

鍙互閫氳繃  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銆�澶氫釜楠岃瘉鍧楁椂锛屽畠浠殑鐩稿椤哄簭寰堥噸瑕併�

 

Full verification

涓ユ牸楠岃瘉琚皟鐢ㄧ殑鏂规硶鏈夊摢浜涗釜锛屼笉鑳藉涔熶笉鑳藉皯锛屼絾椤哄簭鏃犲叧銆�/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>

Full verification in order

 

@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;

 

Restricting the set of mocked types to be fully verified  鍙鎸囧畾鐨�mock 瀵硅薄鎴栫被鍨嬪仛瀹屽叏楠岃瘉

@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 瀵硅薄鎴栫被鍨嬨� 

 

Verifying that no invocations occurred

鍙渶瑕佷娇鐢ㄧ┖鐨�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 涓嶈兘鏈夊叾浠栨柟娉曡皟鐢ㄣ� 

  

Verifying unspecified invocations that should not happen

涓婇潰宸茬粡鎻愬埌锛屽鏋滆淇濊瘉鏌愪簺鏂规硶涓嶈璋冪敤鐨勮瘽锛屼笉蹇呭湪 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
   }};
}

 

Capturing invocation arguments for verification  鑾峰緱褰曞埗闃舵浼犲叆鐨勫弬鏁�/h2>

瀵瑰弬鏁伴獙璇佸線寰�槸閫氳繃涔嬪墠鎻愬埌鐨勫弬鏁板尮閰嶆潵瀹炵幇鐨勶紝杩欓噷鎻愬埌浜嗗彟澶栦竴绉嶆洿鐩磋鐨勬柟寮忥紝鎶婂弬鏁版崟鎹夊埌鐒跺悗鍋氳繘涓�鐨勯獙璇併� 

杩欐椂瑕佷娇鐢�withCapture(...) 浣滀负鍙傛暟鍖归厤鍣�鎰忓懗鐫�紝姝ゆ椂鍙幏寰楀叾鍊硷紝涓嶅仛鍖归厤)銆�鏈変笁绉嶄娇鐢ㄦ儏鍐碉細

  1. 鎹曟崏鍙皟鐢ㄤ竴娆$殑鏂规硶鐨勫弬鏁�锛歍 withCapture() , 浣跨敤 withCapture() 鍖归厤鍙傛暟锛岀劧鍚庡彇杩斿洖鍊硷紱
  2. 鎹曟崏璋冪敤澶氭鐨勬柟娉曠殑鍙傛暟锛�T withCapture(List<T>) 锛�鍙栦紶鍏ョ殑 list 鍙傛暟锛�/li>
  3. 鎹曟崏鏋勯�鏂规硶鐨勫弬鏁帮細 List<T> withCapture(T) 

鎹曟崏鍙皟鐢ㄤ竴娆$殑鏂规硶鐨勫弬鏁帮細

@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 }

 

Delegates: specifying custom results  瀹氬埗result 鐨勮绠楅�杈�/h2>

鎴戜滑涔嬪墠宸茬粡鐪嬪埌濡備綍閫氳繃 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);
}

Cascading mocks   绾ц仈 mock 

杩欑鎯呭喌锛�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, 闆嗗悎-闀垮害涓�鐨勯泦鍚堛�

 

Cascading static factory methods   

杩欑绾ц仈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()锛屽畠灏卞彲鐢ㄣ� 

 

Cascading self-returning methods

娴佸紡(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>

 

Accessing private members  璁块棶绉佹湁鎴愬憳

鏈夋椂鎴戜滑闇�璁块棶琚祴瀵硅薄鍜宮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>

Partial mocking  mock 閮ㄥ垎鏂规硶

缂虹渷鐨勶紝@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鐨勬柟娉曘�  

Capturing implementation classes and instances   鎹曟崏鎵�湁鐨勫疄鐜扮被鍜屽疄渚�/h2>

涓嬮潰杩欎釜渚嬪瓙骞朵笉瀹炵敤锛�浣嗚兘鏄庝簡鍦拌鏄庨棶棰樸�鐪嬪彂琛岀増闅忛檮鐨勭ず渚嬪垯鏇村疄鐢紝鍙弬鑰冨叾涓�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鍛紒

Mocking unspecified implementation classes  浣跨粰瀹氬熀绫荤殑瀛愬瓩绫昏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闃舵锛屼笉绠″疄鐜扮被鎴栧疄渚嬫槸鍝釜锛屽彧瑕佽皟鐢ㄨ繖涓柟娉曢兘浼氬彈鍒板奖鍝嶃� 

Specifying behavior for future instances   闄愬畾鍙�@Capturing 褰卞搷鐨勫疄渚嬩釜鏁�nbsp;

@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, 灏界瀹冨彲鑳芥病鏈夎褰曞埗銆� 

Instantiation and injection of tested classes   瀹炰緥鍖栨祴璇曠被鍙婂鍏舵敞鍏ヤ緷璧�/h2>

寰�線锛屼竴涓祴璇曠被寰�線鎿嶄綔涓�釜琚祴鐨勭被銆侸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 鍙橀噺锛屽鏋滅浉鍚岀被鍨嬬殑鏈夊涓紝灏辨牴鎹悕绉板尮閰嶃�

Reusing expectation and verification blocks

鏈�畝鍗曠殑鍔炴硶灏辨槸鎶妋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>

 

Other topics   鍏朵粬涓婚

Mocking multiple interfaces at the same time  mocked 瀵硅薄鍚屾椂瀹炵幇澶氫釜鎺ュ彛

涓嬮潰绀轰緥涓�釜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 鐨勭被鍨嬨� 

Iterated expectations   褰曞埗杩唬杩囩▼

浣跨敤 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 鏉ヨ

Verifying iterations  楠岃瘉杩唬娆℃暟

鍚屾牱鐨�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 涓婂簲鐢ㄥ彲浠ョ被姣旇繖涓緥瀛愩�

 

你可能感兴趣的:(JMockit 鎸囧崡 缈昏瘧)