2013-02-28 30 views
5

Tôi cố gắng để viết một bài kiểm tra đơn vị cho việc này:Làm cách nào để tôi có thể tạo tài nguyên có thể đóng tự động đúng cách?

try (final DatagramChannel channel = helper.createChannel()) { 

... 

} 

Trong thử nghiệm của tôi, tôi nhạo báng các helper (sử dụng Mockito), và nói với helper.createChannel() để trả về một kênh chế giễu.

thử nghiệm này không thành công với

java.lang.NullPointerException 
at java.nio.channels.spi.AbstractInterruptibleChannel.close(AbstractInterruptibleChannel.java:111) 

Tôi hiểu rằng các thiết bị thử-với-các nguồn lực trong Java gọi phương thức close() trong DatagramChannel lúc đi ra khỏi khối try, nhưng không nên đóng cửa() phương pháp trong DatagramChannel giả được gọi khi?

Trình gỡ lỗi cho tôi biết rằng khóa closeLock trong AbstractInterruptibleChannel là rỗng.

Tôi có nên phân lớp DatagramChannel, ghi đè lên phương thức close() trong đó và sau đó giả lập lớp con của tôi thay thế không? Hoặc, tôi đang làm điều gì đó sai trái một cách sâu sắc hơn (mô hình trợ giúp trả về một mô hình)?

Kính trọng, Fredrik Israelsson

mã kiểm tra, theo yêu cầu:

@Mock 
private InetAddress peerAddress; 
@Mock 
private UDPChannelHelper helper; 
@Mock 
private DatagramChannel channel; 

private UDPTransportImpl transport; 

@Before 
public void setUp() throws Exception { 
    MockitoAnnotations.initMocks(this); 
    when(helper.createChannel()).thenReturn(channel); 
    transport = new UDPTransportImpl(peerAddress, 0, helper); 
} 

@Test 
public void testNormalSubmit() throws Exception { 
    transport.submit("Hello"); 
} 

Như bạn thấy, tôi không chỉ định bất kỳ hành vi cho channel.close(). Tôi tin rằng tôi không cần phải, bởi vì close() trả về void.

+0

Bạn có thể hiển thị mã của mình ở nơi bạn đang chế nhạo những mã này không? Bạn cũng đã sửa lỗi và xác nhận rằng helper a) thực sự là một giả lập, b) helper.createChannel() cũng trả về một đối tượng giả lập? – cowls

+0

Đã thêm mã kiểm tra và có, trong trình gỡ lỗi, cả trình trợ giúp và kênh đều thuộc loại BlaBlaBla $$ EnhancerByMockitoWithCGLIB. –

+1

Trang này: http://mockito.googlecode.com/svn/tags/latest/javadoc/org/mockito/Mockito.html#doNothing%28%29 tuyên bố rõ ràng rằng "các phương pháp void trên mocks không làm gì theo mặc định!". Vì vậy, chúng tôi chỉ có thể giả định rằng khối cuối cùng không thực hiện trên mô hình. Mã của bạn có vẻ tốt với tôi mặc dù vậy Im không chắc chắn lý do tại sao đó là .. bạn chắc chắn không cần phải phân lớp nó cho mình mặc dù. – cowls

Trả lời

6

Bạn đang chế nhạo một lớp thực tế DatagramChannel, mở rộng AbstractInterruptibleChannel. Tuy nhiên, AbstractInterruptibleChannel.close là cuối cùng và Mockito hiện không thể giả lập mã cuối cùng. Điều này giải thích tại sao bạn có NPE trong mã.

Tôi phải nhắc bạn rằng người ta thường chấp nhận rằng các loại chế nhạo bạn không sở hữu là thực hành không tốt. Tôi đã nhìn thấy mọi người làm điều đó và họ đã có những bất ngờ xấu sau này khi việc thực hiện thực sự đã thay đổi, nhưng hành vi giả lập không, vì vậy họ nghĩ rằng mọi thứ đều ổn khi họ cập nhật phiên bản của thư viện.

Vẫn còn nếu bạn muốn tiếp tục theo cách này bởi vì bạn có lý do hợp lệ cho điều đó (và có một số), bạn có thể quay trở lại thay vì mô hình giao diện, như Channel thực sự mở rộng Closeable. Hoặc bạn có thể sử dụng bất kỳ giao diện nào khác mà bạn cần tương tác với giao diện đã có trong DatagramChannel. Ngoài ra nếu bạn cần nhiều hơn một giao diện chỉ cần sử dụng mock(Channel.class, withSetting().extraInterfaces(...)).

Hy vọng rằng sẽ giúp Chúc mừng, Brice

1

Giữ dành cho dù bạn nên làm điều này hay không, một cách để bạn có thể làm việc xung quanh vấn đề này là do "sửa chữa" các AbstractInterruptibleChannel giả dụ (cho dù là một FileChannel, một DatagramChannel, vv) bằng cách cung cấp một đối tượng cho trường closeLock được sử dụng để đồng bộ hóa cuộc gọi gần.

private static void fixChannelMock(AbstractInterruptibleChannel mockFileChannel) throws Exception { 
    Field closeLockField = AbstractInterruptibleChannel.class.getDeclaredField("closeLock"); 
    closeLockField.setAccessible(true); 
    closeLockField.set(mockFileChannel, new Object()); 
} 

Hãy chuẩn bị để sửa mã trên trên bản phát hành Java nhỏ mặc dù việc triển khai nội bộ AbstractInterruptibleChannel có thể thay đổi.

0

Tôi đã có cùng một vấn đề và sử dụng gián điệp (..) thay vì giả (..) đã làm việc cho tôi.Tôi đã cố gắng để mô phỏng một lỗi trong khi cắt xén một tập tin và nếu hệ thống của tôi đã xử lý lỗi cho phù hợp.

FileChannel fileChannel = spy(FileChannel.class); 
mockStatic(FileChannel.class); 
when(FileChannel.open(eq(filePath), eq(StandardOpenOption.WRITE))).thenReturn(fileChannel); 
when(fileChannel.truncate(1000L)).thenThrow(new IOException("Unable to truncate file")); 

... 

// Snippet being tested! 
fileChannel = FileChannel.open(filePath, StandardOpenOption.WRITE); 
fileChannel.truncate(1000L); // Will throw the exception! 
Các vấn đề liên quan