2013-05-14 23 views
45

Đây là câu hỏi chung về Bu lông Thử nghiệm Đơn vị và Vòi trong Cấu trúc liên kết bão được viết bằng Java.Kiểm tra Bu lông và Vòi Bão

thực hành đề nghị và kim chỉ nam cho unit-testing là gì (JUnit?) Bu lôngvòi?

Ví dụ, tôi có thể viết một bài kiểm tra JUnit cho một Bolt, nhưng mà không hiểu đầy đủ khuôn khổ (như vòng đời của một Bolt) và những tác động serialization, dễ dàng mắc sai lầm khi Constructor dựa trên sự sáng tạo của phi serializable biến thành viên. Trong JUnit, bài kiểm tra này sẽ trôi qua, nhưng trong một cấu trúc liên kết, nó sẽ không hoạt động. Tôi hoàn toàn tưởng tượng có nhiều điểm kiểm tra cần xem xét (ví dụ như ví dụ này với vòng đời Serialization &).

Vì vậy, nó là khuyến cáo rằng nếu bạn sử dụng JUnit dựa unit tests, bạn chạy một topo giả nhỏ (LocalMode?) Và kiểm tra các hợp đồng ngụ ý cho Bolt (hoặc Spout) dưới Topology đó? Hoặc, là nó OK để sử dụng JUnit, nhưng ngụ ý được rằng chúng ta phải mô phỏng vòng đời của một Bolt (tạo ra nó, gọi prepare(), chế giễu một Config, vv) một cách cẩn thận? Trong trường hợp này, một số điểm kiểm tra chung cho lớp đang được kiểm tra (Bolt/Spout) cần xem xét là gì?

Các nhà phát triển khác đã làm gì, liên quan đến việc tạo các thử nghiệm đơn vị thích hợp?

Tôi nhận thấy có một API thử nghiệm Topology (Xem: https://github.com/xumingming/storm-lib/blob/master/src/jvm/storm/TestingApiDemo.java). Tốt hơn là sử dụng một số API đó và đứng lên "Kiểm tra cấu trúc liên kết" cho từng cá nhân Bolt & Spout (và xác minh hợp đồng tiềm ẩn mà Bolt cung cấp cho, ví dụ - đó là đầu ra Đã khai báo)?

Cảm ơn

+0

Bạn đã bao giờ quyết định một cách tiếp cận? –

+0

Vâng, tôi đã đọc các câu trả lời bên dưới. Có vẻ như có một số hướng dẫn chung, nhưng không có gì đặt trong đá. Tôi sẽ để câu hỏi mở ra một chút lâu hơn để xem liệu có ai khác có suy nghĩ gì sau đó đóng nó lại không. Tôi thích cả hai cách tiếp cận sử dụng API kiểm tra (TestingApiDemo.java) cũng như câu trả lời của bạn để chế nhạo phụ thuộc, @ChrisGerken. – Jack

Trả lời

8

Cách tiếp cận của chúng tôi là sử dụng nhà xây dựng-tiêm của một nhà máy serializable vào vòi/bu lông. Các vòi/bu lông sau đó tư vấn cho nhà máy trong phương pháp mở/chuẩn bị của nó.Trách nhiệm duy nhất của nhà máy là đóng gói lấy phụ thuộc của vòi/bu lông trong một thời gian nối tiếp. Thiết kế này cho phép thử nghiệm đơn vị của chúng tôi để tiêm các nhà máy giả/thử nghiệm/mô hình, khi được tư vấn, trả lại các dịch vụ giả lập. Bằng cách này, chúng tôi có thể thu hẹp đơn vị kiểm tra các vòi/bu lông bằng cách sử dụng mocks, ví dụ: Mockito.

Dưới đây là ví dụ chung về bu lông và thử nghiệm cho nó. Tôi đã bỏ qua việc triển khai nhà máy UserNotificationFactory vì nó phụ thuộc vào đơn đăng ký của bạn. Bạn có thể sử dụng các trình định vị dịch vụ để lấy các dịch vụ, cấu hình tuần tự, cấu hình có thể truy cập HDFS, hoặc thực sự bất kỳ cách nào để có được các dịch vụ chính xác, miễn là nhà máy có thể thực hiện nó sau chu kỳ serde. Bạn nên bao gồm serialization của lớp đó.

bolt

public class NotifyUserBolt extends BaseBasicBolt { 
    public static final String NAME = "NotifyUser"; 
    private static final String USER_ID_FIELD_NAME = "userId"; 

    private final UserNotifierFactory factory; 
    transient private UserNotifier notifier; 

    public NotifyUserBolt(UserNotifierFactory factory) { 
    checkNotNull(factory); 

    this.factory = factory; 
    } 

    @Override 
    public void prepare(Map stormConf, TopologyContext context) { 
    notifier = factory.createUserNotifier(); 
    } 

    @Override 
    public void execute(Tuple input, BasicOutputCollector collector) { 
    // This check ensures that the time-dependency imposed by Storm has been observed 
    checkState(notifier != null, "Unable to execute because user notifier is unavailable. Was this bolt successfully prepared?"); 

    long userId = input.getLongByField(PreviousBolt.USER_ID_FIELD_NAME); 

    notifier.notifyUser(userId); 

    collector.emit(new Values(userId)); 
    } 

    @Override 
    public void declareOutputFields(OutputFieldsDeclarer declarer) { 
    declarer.declare(new Fields(USER_ID_FIELD_NAME)); 
    } 
} 

thử nghiệm

public class NotifyUserBoltTest { 

    private NotifyUserBolt bolt; 

    @Mock 
    private TopologyContext topologyContext; 

    @Mock 
    private UserNotifier notifier; 

    // This test implementation allows us to get the mock to the unit-under-test. 
    private class TestFactory implements UserNotifierFactory { 

    private final UserNotifier notifier; 

    private TestFactory(UserNotifier notifier) { 
     this.notifier = notifier; 
    } 

    @Override 
    public UserNotifier createUserNotifier() { 
     return notifier; 
    } 
    } 

    @Before 
    public void before() { 
    MockitoAnnotations.initMocks(this); 

    // The factory will return our mock `notifier` 
    bolt = new NotifyUserBolt(new TestFactory(notifier)); 
    // Now the bolt is holding on to our mock and is under our control! 
    bolt.prepare(new Config(), topologyContext); 
    } 

    @Test 
    public void testExecute() { 
    long userId = 24; 
    Tuple tuple = mock(Tuple.class); 
    when(tuple.getLongByField(PreviousBolt.USER_ID_FIELD_NAME)).thenReturn(userId); 
    BasicOutputCollector collector = mock(BasicOutputCollector.class); 

    bolt.execute(tuple, collector); 

    // Here we just verify a call on `notifier`, but we could have stubbed out behavior befor 
    // the call to execute, too. 
    verify(notifier).notifyUser(userId); 
    verify(collector).emit(new Values(userId)); 
    } 
} 
+0

Tôi biết đây là một bài cũ để tha thứ cho tôi nếu bạn đã chuyển từ dự án này :) Tôi tự hỏi liệu các thử nghiệm để xác minh (notifier) ​​.notifyUser (userId); thông qua. Tôi thấy rằng serialization và deserialization mà Storm thực hiện trên nhà máy gây ra một trình thông báo giả lập mới được khởi tạo. Do đó, trình thông báo mô phỏng ban đầu không nhận được bất kỳ tương tác nào. Đây có phải là trường hợp của bạn không? – ilana917

+0

@ ilana917 Bão và mocks không nên tương tác. Mục đích của mẫu này là viết mã theo cách cho phép bạn kiểm tra mã riêng biệt với thời gian chạy của Storm. Trong thử nghiệm, không cần serialization. Giữa các dòng 'NotifyUserBolt' và' bolt.prepare' mới, không có serialization trong thử nghiệm. Trong thời gian chạy Storm, Storm sẽ tuần tự hóa Bolt. –

+0

Cảm ơn @ carl-g, đó là hướng tôi sẽ làm. Tôi đang ở giữa một nhà tái cấu trúc lớn và ban đầu dự định chạy kết thúc bão để kết thúc như một loại thử nghiệm tích hợp bằng cách sử dụng mocks cho các kết nối bên ngoài, nhưng đơn vị kiểm tra logic của chúng tôi có ý nghĩa hơn. – ilana917

11

Một cách tiếp cận chúng tôi đã đưa là để di chuyển hầu hết các logic ứng dụng ra khỏi bu lông và vòi và thành các đối tượng mà chúng ta sử dụng để làm các việc nâng nặng bởi instantiating và sử dụng chúng thông qua giao diện tối thiểu. Sau đó, chúng tôi thực hiện thử nghiệm đơn vị trên các đối tượng đó và thử nghiệm tích hợp, mặc dù điều này không để lại một khoảng trống.

+2

trong khi những gì bạn nói là đúng, và một ý tưởng tốt, nó không nhận được sự quan tâm của OP trong việc tìm kiếm những thứ như vấn đề serialization trước khi thực hiện triển khai sản xuất. –

+0

Cảm ơn bạn đã phản hồi! Tôi tò mò về những gì bạn có nghĩa là "để lại một khoảng cách"? – taylorcressy

+0

khoảng cách là tại giao diện giữa bão và các đối tượng của chúng ta. phần đó không được kiểm tra kỹ lưỡng bởi vì nó chỉ xuất hiện trong các bài kiểm tra tích hợp, tốn kém để làm cho đầy đủ, do đó, có một số mã kết nối không được che phủ đầy đủ –

1

Hóa ra là khá dễ dàng để thử các đối tượng bão như OutputDeclarer, Tuple và OutputFieldsDeclarer. Trong số đó, chỉ OutputDeclarer mới thấy bất kỳ tác dụng phụ nào để mã lớp lớp mô phỏng OutputDeclarer có thể trả lời bất kỳ bộ dữ liệu và neo nào được phát ra, ví dụ. Sau đó, lớp thử nghiệm của bạn có thể sử dụng các cá thể của các lớp mô hình đó để dễ dàng định cấu hình một cá thể bolt/spout, gọi nó và xác nhận các tác dụng phụ mong đợi.

14

Kể từ khi cơ sở kiểm tra đơn vị phiên bản 0.8.1 Storm đã tiếp xúc qua Java:

Đối với một ví dụ làm thế nào để sử dụng API này có một cái nhìn ở đây:

+6

Thực sự là một API tốt, mặc dù tài liệu ít hơn một chút sẽ giúp rất nhiều trong việc hiểu nó. –

+0

Đây là một bài đăng trên blog có thể hữu ích: http://www.pixelmachine.org/2011/12/17/Testing-Storm-Topologies.html Nó giải thích API clojure để thử nghiệm bão, nhưng về cơ bản là giống nhau như API Java. – asmaier