Panduan untuk JMockit Harapan

1. Pengenalan

Artikel ini adalah ansuran kedua dalam siri JMockit. Anda mungkin ingin membaca artikel pertama kerana kami menganggap bahawa anda sudah biasa dengan asas-asas JMockit.

Hari ini kita akan lebih mendalam dan menumpukan pada harapan. Kami akan menunjukkan cara menentukan padanan argumen yang lebih spesifik atau generik dan cara yang lebih maju untuk menentukan nilai.

2. Pemadanan Nilai Hujah

Pendekatan berikut berlaku untuk Harapan dan juga Pengesahan .

2.1. Medan "Mana-mana"

JMockit menawarkan sekumpulan bidang utiliti untuk menjadikan padanan argumen lebih umum. Salah satu utiliti ini adalah bidang anyX .

Ini akan memeriksa bahawa ada nilai yang dilalui dan ada satu untuk setiap jenis primitif (dan kelas pembungkus yang sesuai), satu untuk tali, dan yang "universal" salah satu jenis Objek .

Mari lihat contoh:

public interface ExpectationsCollaborator { String methodForAny1(String s, int i, Boolean b); void methodForAny2(Long l, List lst); } @Test public void test(@Mocked ExpectationsCollaborator mock) throws Exception { new Expectations() {{ mock.methodForAny1(anyString, anyInt, anyBoolean); result = "any"; }}; Assert.assertEquals("any", mock.methodForAny1("barfooxyz", 0, Boolean.FALSE)); mock.methodForAny2(2L, new ArrayList()); new FullVerifications() {{ mock.methodForAny2(anyLong, (List) any); }}; }

Anda mesti mengambil kira bahawa semasa menggunakan bidang apa pun , anda perlu memasukkannya ke jenis yang diharapkan. Senarai bidang yang lengkap terdapat dalam dokumentasi.

2.2. Kaedah "Dengan"

JMockit juga menyediakan beberapa kaedah untuk membantu pemadanan argumen generik. Itu adalah kaedah withX .

Ini membolehkan pencocokan sedikit lebih maju daripada medan anyX . Kita dapat melihat contoh di sini di mana kita akan menentukan jangkaan untuk kaedah yang akan dicetuskan dengan rentetan yang mengandungi foo , bilangan bulat tidak sama dengan 1, Boolean yang tidak kosong dan mana-mana contoh kelas Senarai :

public interface ExpectationsCollaborator { String methodForWith1(String s, int i); void methodForWith2(Boolean b, List l); } @Test public void testForWith(@Mocked ExpectationsCollaborator mock) throws Exception { new Expectations() {{ mock.methodForWith1(withSubstring("foo"), withNotEqual(1)); result = "with"; }}; assertEquals("with", mock.methodForWith1("barfooxyz", 2)); mock.methodForWith2(Boolean.TRUE, new ArrayList()); new Verifications() {{ mock.methodForWith2(withNotNull(), withInstanceOf(List.class)); }}; }

Anda boleh melihat senarai lengkap withX kaedah dokumentasi JMockit ini.

Perhatikan bahawa yang istimewa dengan (Delegate) dan withArgThat (Matcher) akan dilindungi dalam subseksyen mereka sendiri.

2.3. Null Bukan Null

Sesuatu yang baik untuk dipahami lebih cepat daripada yang kemudian adalah bahawa null tidak digunakan untuk menentukan argumen yang null telah disampaikan kepada ejekan.

Sebenarnya, null digunakan sebagai gula sintaksis untuk menentukan bahawa objek apa pun akan dilalui (jadi ia hanya dapat digunakan untuk parameter jenis rujukan). Untuk mengesahkan secara khusus bahawa parameter tertentu menerima rujukan nol , matcher withNull () dapat digunakan.

Untuk contoh seterusnya, kami akan menentukan tingkah laku untuk ejekan, yang harus dicetuskan ketika argumen yang dilalui adalah: tali apa pun, Senarai apa pun, dan rujukan nol :

public interface ExpectationsCollaborator { String methodForNulls1(String s, List l); void methodForNulls2(String s, List l); } @Test public void testWithNulls(@Mocked ExpectationsCollaborator mock){ new Expectations() {{ mock.methodForNulls1(anyString, null); result = "null"; }}; assertEquals("null", mock.methodForNulls1("blablabla", new ArrayList())); mock.methodForNulls2("blablabla", null); new Verifications() {{ mock.methodForNulls2(anyString, (List) withNull()); }}; }

Perhatikan perbezaannya: null bermaksud senarai apa pun dan denganNull () bermaksud nol merujuk kepada senarai. Khususnya, ini mengelakkan perlunya membuang nilai ke jenis parameter yang dinyatakan (lihat bahawa argumen ketiga harus dilemparkan tetapi bukan yang kedua).

Satu-satunya syarat untuk dapat menggunakan ini adalah bahawa sekurang-kurangnya satu pemadan argumen eksplisit telah digunakan untuk jangkaan (sama ada kaedah dengan kaedah atau bidang apa pun ).

2.4. Medan "Kali"

Kadang-kadang, kita mahu mengehadkan jumlah panggilan yang dijangkakan untuk kaedah ejekan. Untuk ini, JMockit mempunyai waktu kata yang dikhaskan , minTimes dan maxTimes (ketiga-tiganya membenarkan bilangan bulat bukan negatif sahaja).

public interface ExpectationsCollaborator { void methodForTimes1(); void methodForTimes2(); void methodForTimes3(); } @Test public void testWithTimes(@Mocked ExpectationsCollaborator mock) { new Expectations() {{ mock.methodForTimes1(); times = 2; mock.methodForTimes2(); }}; mock.methodForTimes1(); mock.methodForTimes1(); mock.methodForTimes2(); mock.methodForTimes3(); mock.methodForTimes3(); mock.methodForTimes3(); new Verifications() {{ mock.methodForTimes3(); minTimes = 1; maxTimes = 3; }}; }

Dalam contoh ini, kami telah menentukan bahawa tepat dua doa (bukan satu, bukan tiga, tepat dua) kaedahForTimes1 () harus dilakukan dengan menggunakan garis masa = 2; .

Kemudian kami menggunakan tingkah laku lalai (jika tidak ada kekangan pengulangan diberikan minTimes = 1; digunakan) untuk menentukan bahawa sekurang-kurangnya satu permintaan akan dilakukan ke methodForTimes2 ().

Terakhir, menggunakan minTimes = 1; diikuti oleh maxTimes = 3; kami menentukan bahawa antara satu dan tiga pemanggilan akan berlaku untuk methodForTimes3 () .

Perhatikan bahawa kedua minTimes dan maxTimes dapat ditentukan untuk jangkaan yang sama, selagi minTimes ditetapkan terlebih dahulu. Sebaliknya, masa hanya boleh digunakan sendiri.

2.5. Pemadanan Hujah Khusus

Kadang-kadang pemadanan argumen tidak langsung seperti hanya menentukan nilai atau menggunakan beberapa utiliti yang telah ditentukan ( anyX atau withX ).

Untuk kes tersebut, JMockit bergantung pada antara muka Hamcrest's Matcher . Anda hanya perlu menentukan pemadan untuk senario ujian tertentu dan menggunakan pemadan itu dengan panggilan withArgThat () .

Mari kita lihat contoh untuk memadankan kelas tertentu dengan objek lulus:

public interface ExpectationsCollaborator { void methodForArgThat(Object o); } public class Model { public String getInfo(){ return "info"; } } @Test public void testCustomArgumentMatching(@Mocked ExpectationsCollaborator mock) { new Expectations() {{ mock.methodForArgThat(withArgThat(new BaseMatcher() { @Override public boolean matches(Object item) { return item instanceof Model && "info".equals(((Model) item).getInfo()); } @Override public void describeTo(Description description) { } })); }}; mock.methodForArgThat(new Model()); }

3. Mengembalikan Nilai

Sekarang mari kita lihat nilai pulangan; ingat bahawa pendekatan berikut hanya berlaku untuk Harapan kerana tiada nilai pulangan yang dapat ditentukan untuk Pengesahan .

3.1. Hasil dan Pulangan (…)

Semasa menggunakan JMockit, anda mempunyai tiga cara berbeza untuk menentukan hasil yang diharapkan dari pemanggilan kaedah mengejek. Dari ketiga-tiganya, kita akan membincangkan sekarang mengenai dua yang pertama (yang paling mudah) yang pasti merangkumi 90% kes penggunaan seharian.

These two are the result field and the returns(Object…) method:

  • With the result field, you can define one return value for any non-void returning mocked method. This return value can also be an exception to be thrown (this time working for both non-void and void returning methods).
    • Several result field assignations can be done in order to return more than one value for more than one method invocations (you can mix both return values and errors to be thrown).
    • The same behaviour will be achieved when assigning to result a list or an array of values (of the same type than the return type of the mocked method, NO exceptions here).
  • The returns(Object…) method is syntactic sugar for returning several values of the same time.

This is more easily shown with a code snippet:

public interface ExpectationsCollaborator{ String methodReturnsString(); int methodReturnsInt(); } @Test public void testResultAndReturns(@Mocked ExpectationsCollaborator mock) { new Expectations() {{ mock.methodReturnsString(); result = "foo"; result = new Exception(); result = "bar"; returns("foo", "bar"); mock.methodReturnsInt(); result = new int[]{1, 2, 3}; result = 1; }}; assertEquals("Should return foo", "foo", mock.methodReturnsString()); try { mock.methodReturnsString(); fail("Shouldn't reach here"); } catch (Exception e) { // NOOP } assertEquals("Should return bar", "bar", mock.methodReturnsString()); assertEquals("Should return 1", 1, mock.methodReturnsInt()); assertEquals("Should return 2", 2, mock.methodReturnsInt()); assertEquals("Should return 3", 3, mock.methodReturnsInt()); assertEquals("Should return foo", "foo", mock.methodReturnsString()); assertEquals("Should return bar", "bar", mock.methodReturnsString()); assertEquals("Should return 1", 1, mock.methodReturnsInt()); }

In this example, we have defined that for the first three calls to methodReturnsString() the expected returns are (in order) “foo”, an exception and “bar”. We achieved this using three different assignations to the result field.

Then on line 14, we defined that for the fourth and fifth calls, “foo” and “bar” should be returned using the returns(Object…) method.

For the methodReturnsInt() we defined on line 13 to return 1, 2 and lastly 3 by assigning an array with the different results to the result field and on line 15 we defined to return 1 by a simple assignation to the result field.

As you can see there are several ways of defining return values for mocked methods.

3.2. Delegators

To end the article we're going to cover the third way of defining the return value: the Delegate interface. This interface is used for defining more complex return values when defining mocked methods.

We're going to see an example to simply the explaining:

public interface ExpectationsCollaborator { int methodForDelegate(int i); } @Test public void testDelegate(@Mocked ExpectationsCollaborator mock) { new Expectations() {{ mock.methodForDelegate(anyInt); result = new Delegate() { int delegate(int i) throws Exception { if (i < 3) { return 5; } else { throw new Exception(); } } }; }}; assertEquals("Should return 5", 5, mock.methodForDelegate(1)); try { mock.methodForDelegate(3); fail("Shouldn't reach here"); } catch (Exception e) { } } 

The way to use a delegator is to create a new instance for it and assign it to a returns field. In this new instance, you should create a new method with the same parameters and return type than the mocked method (you can use any name for it). Inside this new method, use whatever implementation you want in order to return the desired value.

In the example, we did an implementation in which 5 should be returned when the value passed to the mocked method is less than 3 and an exception is thrown otherwise (note that we had to use times = 2; so that the second invocation is expected as we lost the default behaviour by defining a return value).

Nampaknya kodnya agak banyak, tetapi untuk beberapa kes, ini adalah satu-satunya cara untuk mencapai hasil yang kita mahukan.

4. Kesimpulan

Dengan ini, kami secara praktikal menunjukkan semua yang kami perlukan untuk mewujudkan jangkaan dan pengesahan untuk ujian harian kami.

Sudah tentu kami akan menerbitkan lebih banyak artikel di JMockit, jadi nantikan lebih banyak lagi.

Dan, seperti biasa, pelaksanaan penuh tutorial ini boleh didapati di projek GitHub.

4.1. Artikel dalam Siri

Semua artikel siri ini:

  • JMockit 101
  • Panduan untuk JMockit Harapan
  • Penggunaan Lanjutan JMockit