2011-12-04 4 views
5

Tôi muốn xác minh thứ tự cụ thể trong đó các ký tự để đảm bảo rằng chúng không bị cắt xén. Tôi đã thử viết nó bằng cách sử dụng InOrder nhưng có vẻ như không hoạt động, hoặc ít nhất là trong Mockito 1.8.5.Làm thế nào để kiểm tra trong Mockito cho thứ tự cụ thể của các cuộc gọi với cùng một đối số?

@Test 
public void inOrderTest() throws IOException{ 
    final String message = "Hello World!\n"; 

    for(char c : message.toCharArray()) 
     mockWriter.write(c); 

    final InOrder inOrder = inOrder(mockWriter); 
    for(char c : message.toCharArray()) 
     inOrder.verify(mockWriter).write(c); 
    inOrder.verifyNoMoreInteractions(); 
} 

Bài kiểm tra trên không thành công với thông điệp:

Verification in order failure: 
mockWriter.write(108); 
Wanted 1 time: 
-> at  org.bitbucket.artbugorski.brainfuj.interpreter.InterpreterTest.inOrderTest(InterpreterTest.java:62) 
But was 3 times. Undesired invocation: 
-> at org.bitbucket.artbugorski.brainfuj.interpreter.InterpreterTest.inOrderTest(InterpreterTest.java:58) 

Làm sao người ta viết một bài kiểm tra Mockito cho điều đó?


EDIT: Filed như lỗi http://code.google.com/p/mockito/issues/detail?id=296

Trả lời

19

Lời xin lỗi của tôi đối với những người trả lời trước; nhưng theo ý kiến ​​của tôi, việc sử dụng một câu trả lời sẽ bay một chút khi đối mặt với một trong những ý tưởng cơ bản của Mockito, cụ thể là việc rà soát và xác minh là hai quy trình hoàn toàn riêng biệt. Mockito có các tính năng cho stubbing và các tính năng để xác minh, và các nhà sản xuất của Mockito đã có một nỗ lực để giữ hai riêng biệt. Các câu trả lời được dự định cho stubbing; và trong khi có một vài trường hợp Câu trả lời là cách tốt nhất để xác minh, tôi không tin đây là một trong số đó.

Tôi sẽ sử dụng một ArgumentCaptor thay vì Trả lời. Tôi sẽ viết một phương pháp như thế này trong lớp thi, sau đó gọi nó với "Hello world" làm đối số. Lưu ý rằng tôi chưa thử nghiệm điều này, vì vậy nó có thể chứa lỗi chính tả.

private void verifyCharactersWritten(String expected){ 
    ArgumentCaptor<Character> captor = ArgumentCaptor.forClass(Character.class); 
    verify(mockWriter, times(expected.length())).write(captor.capture()); 
    assertEquals(Arrays.asList(expected.toCharArray()), captor.getAllValues()); 
} 

Hy vọng điều này sẽ hữu ích.

+0

Slick. Tôi không biết về tính năng đó của Captor arg. –

+0

đôi khi dễ dàng kiểm tra kết quả/đầu ra/kết quả (nếu có thể), sau đó đọc mã kiểm tra này. Tôi có nghĩa là nếu chúng ta biết rằng nó sẽ trả lại cho chúng tôi: "một số sting chúng tôi mong đợi" cho các đối số có thể. – ses

4

xác nhận để là một khái niệm tách biệt với bao nhiêu lần một cái gì đó được thực hiện, vì vậy khi bạn nhận được để các 'l' và nói Mockito để xác minh rằng nó đã xảy ra, nó vượt qua kiểm tra theo thứ tự, nhưng không thành công vì cuộc gọi 'l' đã được thực hiện ba lần, và bạn (ngầm) nói với nó để mong đợi nó chỉ một lần. Đó là một điều tôi chưa từng thấy trước đây ở Mockito, nhưng bất cứ lúc nào nó xảy ra, tôi quyết định rằng bài kiểm tra của tôi đã được viết kém và khi tôi sửa nó, vấn đề sẽ biến mất. Trong trường hợp của bạn, tôi muốn nói đó là cách quá mức cần thiết để xác minh từng ký tự được ghi vào Writer. Nếu bạn muốn xác minh tin nhắn đã được gửi chính xác, bạn nên so sánh thông điệp đầu vào với thông điệp đầu ra. Trong ví dụ của bạn, điều đó có thể đòi hỏi phải sử dụng một StringWriter thay vì chế nhạo một nhà văn. Sau đó, khi kết thúc thử nghiệm của bạn chỉ trông giống như

assertThat(stringWriter.toString(), equalTo(message)); 

Nếu bạn thực sự phải làm những gì bạn đang làm, tất cả những gì có thể gợi ý là đào vào mã Mockito để xem nếu có một cách để làm cho nó xảy ra và có thể nộp báo cáo lỗi để xem họ nói gì về nó.

+0

Tôi viết một máy ảo/thông dịch viên vì vậy tôi/O xảy ra một ký tự tại một thời gian, không có khái niệm về toàn bộ thông điệp, chỉ là một loạt các ký tự riêng lẻ được in ra. Tôi đang cố gắng để đảm bảo rằng trong quá trình in ấn chúng tôi không làm hỏng bất cứ điều gì (có, tôi đang thực hiện bộ nhớ trong của riêng tôi). Thông báo tôi đang cố gắng xác minh là "Hello World" vì vậy tôi không nghĩ rằng nó quá mức cần thiết để kiểm tra xem một tin nhắn như vậy có thể được in chính xác, ký tự theo ký tự hay không. :) – ArtB

+1

Xin chào, nếu thử nghiệm này rất quan trọng, nghĩa là bạn nên kiểm tra xem bạn đang làm gì, tôi đồng ý với @Ryan, bạn nên so sánh đầu vào so với đầu ra. Khi stubbing nhà văn của bạn, sử dụng một câu trả lời tùy chỉnh mà sẽ gắn thêm char trong một 'StringBuilder' thông thường, sau đó so sánh nó với đầu vào' 'Hello World '' của chúng ta. Ngoài ra, bạn có thể có thể viết một trình phù hợp tùy chỉnh; một cái gì đó như 'inOrder.verify (mockWriter, times (11)). write (charsThatMatchInOrder (" Hello World "));', ** tuy nhiên thử nghiệm này có thể phá vỡ dễ dàng nếu chuỗi thay đổi! ** – Brice

+0

@Bắt tốt đoạn mã được mô phỏng/giải thích là một phần của đầu vào thử nghiệm vì vậy có nếu thay đổi nó được kỳ vọng rằng thử nghiệm sẽ thất bại. – ArtB

0

Tôi hiện đang thực hiện việc này với Câu trả lời tùy chỉnh.

final List<Integer> writtenChars = new ArrayList<>(); 
willAnswer(
     new Answer(){ 
      @Override 
      public Object answer(final InvocationOnMock invocation)throws Throwable { 
       final int arg = (int) invocation.getArguments()[0]; 
       writtenChars.add(arg); 
       return null; 
      } 
     } 
    ).given(mockWriter).write(anyInt()); 

Sau đó, sau khi chạy các phương pháp mong muốn tôi thử nghiệm với chuỗi dự kiến ​​trong danh sách.

final Iterator<Integer> writtenCharItr = writtenChars.iterator(); 
for(int charInt : "Hello World!\n".toCharArray()) 
    assertThat( charInt, is(writtenCharItr.next()) ); 
assertThat("There are no more chars.", writtenCharItr.hasNext(), is(false)); 
verify(mockWriter).flush(); 

Mặc dù điều này sẽ không hoạt động nếu bạn quan tâm đến nhiều hơn một lần gọi phương thức, trừ khi bạn ghi lại trong danh sách mà phương pháp đã được gọi, vv


EDIT: lời xin lỗi đến Brice bạn dường như đã độc lập đến giải pháp này ngoại trừ độc lập và tốt hơn bằng cách sử dụng một số StringBuilder thay vì List, mặc dù đối với trường hợp chung, Danh sách hoạt động tốt hơn.

+0

Tôi đang nghĩ gì ... Một ArgumentCaptor là cách tốt hơn, về cơ bản nó làm những gì câu trả lời tùy chỉnh này đang làm. Phản ứng của David chính xác hơn nhiều theo nhiều cách. – Brice

2

Lý do Mockito hoạt động như thế là sự nhất quán giữa xác minh theo thứ tự và xác minh thông thường. Nói cách khác, nếu chúng tôi đã không thực hiện nó theo cách này API sẽ có được đáng ngạc nhiên theo một cách khác nhau :) Bạn làm cho thương mại giảm được thực hiện khi cố gắng thiết kế một api phong nha.

Vậy ... câu trả lời. Thứ nhất, bạn nên tránh các câu lệnh như vòng lặp (hoặc điều kiện) trong mã thử nghiệm. Lý do là bạn quan tâm rất nhiều cho mã kiểm tra rõ ràng và bảo trì! =)

Nếu chúng tôi xóa vòng lặp khỏi thử nghiệm, chúng tôi không còn có trường hợp sử dụng nữa, mặc dù ... Không có trường hợp sử dụng, thật khó để đưa ra câu trả lời. ArgumentCaptor của David có thể không phải là một ý tưởng tồi.

Hy vọng điều đó sẽ hữu ích!

+0

Tôi không đồng ý. Tôi có thể bỏ vòng lặp và sẽ có cùng một vấn đề. Tôi biết điều này cho một thực tế như tôi đã làm điều đó khi cố gắng để gỡ lỗi này. Thứ hai, nếu nó được gọi là InOrder nhưng nó không thể nói hoàn toàn về thứ tự các cuộc gọi được thực hiện không phải là một sự vi phạm rõ ràng về nguyên tắc của sự ngạc nhiên ít nhất? – ArtB

0

Đây là một thử nghiệm kỳ lạ, nhưng vẫn còn, nó sẽ được hỗ trợ bởi API chế nhạo. Tôi tin rằng nó có thể được hỗ trợ bởi Mockito, vì các API giả mạo khác hỗ trợ nó.

Với Unitils Mock:

Mock<Writer> mockWriter; 

@Test 
public void inOrderTest() throws Exception { 
    Writer writer = mockWriter.getMock(); 
    final String message = "Hello World!\n"; 

    for (char c : message.toCharArray()) 
     writer.write(c); 

    for (char c : message.toUpperCase().toCharArray()) 
     mockWriter.assertInvokedInSequence().write(c); 
    MockUnitils.assertNoMoreInvocations(); 
} 

Hoặc với JMockit (công cụ của riêng tôi):

@Test 
public void inOrderTest(final Writer mockWriter) throws Exception { 
    final String message = "Hello World!\n"; 

    for (char c : message.toCharArray()) 
     mockWriter.write(c); 

    new FullVerificationsInOrder() {{ 
     for (char c : message.toCharArray()) 
      mockWriter.write(c); 
    }}; 
}