2010-06-08 21 views
8

Tôi đang viết kiểm tra đơn vị cho lớp "keo" của ứng dụng và đang gặp khó khăn khi tạo các phép thử xác định cho các phương pháp không đồng bộ cho phép người dùng hủy hoạt động sớm.Hủy hòa nhạc trong thử nghiệm đơn vị dài

Cụ thể, trong một vài phương pháp không đồng bộ, chúng tôi có mã phản ứng với việc hủy cuộc gọi và đảm bảo rằng đối tượng ở trạng thái thích hợp trước khi hoàn thành. Tôi muốn đảm bảo rằng các đường dẫn mã này được kiểm tra.

Một số C# pseudo code khuôn mẫu cho một phương pháp async điển hình trong trường hợp này là như sau:

public void FooAsync(CancellationToken token, Action<FooCompletedEventArgs> callback) 
{ 
    if (token.IsCancellationRequested) DoSomeCleanup0(); 

    // Call the four helper methods, checking for cancellations in between each 
    Exception encounteredException; 
    try 
    { 
     MyDependency.DoExpensiveStuff1(); 
     if (token.IsCancellationRequested) DoSomeCleanup1(); 

     MyDependency.DoExpensiveStuff2(); 
     if (token.IsCancellationRequested) DoSomeCleanup2(); 

     MyDependency.DoExpensiveStuff3(); 
     if (token.IsCancellationRequested) DoSomeCleanup3(); 

     MyDependency.DoExpensiveStuff4(); 
     if (token.IsCancellationRequested) DoSomeCleanup4(); 

    } 
    catch (Exception e) 
    { 
     encounteredException = e; 
    } 

    if (!token.IsCancellationRequested) 
    { 
     var args = new FooCompletedEventArgs(a bunch of params); 
     callback(args); 
    } 
} 

Các giải pháp mà tôi đã đưa ra cho đến nay liên quan đến chế giễu những tiềm ẩn MyDependency hoạt động được bao bọc bởi lớp keo và buộc mỗi người phải ngủ trong một khoảng thời gian tùy ý. Sau đó tôi gọi phương thức async và cho phép kiểm tra đơn vị của tôi ngủ trong một số mili giây trước khi hủy yêu cầu không đồng bộ.

Something như thế này (sử dụng Rhino Mocks là một ví dụ):

[TestMethod] 
public void FooAsyncTest_CancelAfter2() 
{ 
    // arrange 
    var myDependency = MockRepository.GenerateStub<IMyDependency>(); 

    // Set these stubs up to take a little bit of time each so we can orcestrate the cancels 
    myDependency.Stub(x => x.DoExpensiveStuff1()).WhenCalled(x => Thread.Sleep(100)); 
    myDependency.Stub(x => x.DoExpensiveStuff2()).WhenCalled(x => Thread.Sleep(100)); 
    myDependency.Stub(x => x.DoExpensiveStuff3()).WhenCalled(x => Thread.Sleep(100)); 
    myDependency.Stub(x => x.DoExpensiveStuff4()).WhenCalled(x => Thread.Sleep(100)); 

    // act 
    var target = new FooClass(myDependency); 

    CancellationTokenSource cts = new CancellationTokenSource(); 
    bool wasCancelled = false; 

    target.FooAsync(
     cts.Token, 
     args => 
     { 
     wasCancelled = args.IsCancelled; 
     // Some other code to manipulate FooCompletedEventArgs 
     }); 

    // sleep long enough for two operations to complete, then cancel 
    Thread.Sleep(250); 
    cts.Cancel(); 

    // Some code to ensure the async call completes goes here 

    //assert 
    Assert.IsTrue(wasCancelled); 
    // Other assertions to validate state of target go here 
} 

Bên cạnh thực tế rằng việc sử dụng Thread.Sleep trong một thử nghiệm đơn vị làm cho tôi buồn nôn, vấn đề lớn hơn là thỉnh thoảng kiểm tra như thế này không trên máy chủ xây dựng của chúng tôi nếu nó xảy ra dưới tải trọng đáng kể. Cuộc gọi async bị quá xa và quá trình hủy đến quá muộn.

Có ai có thể cung cấp một cách đáng tin cậy hơn về logic hủy kiểm tra đơn vị cho các hoạt động chạy dài như thế này không? Có những câu chuyện mới trên trang chủ.

Trả lời

5

Tôi sẽ cố gắng sử dụng mocks để "mô phỏng" hành vi không đồng bộ theo cách đồng bộ. Thay vì sử dụng

myDependency.Stub(x => x.DoExpensiveStuff1()).WhenCalled(x => Thread.Sleep(100)); 

và sau đó thiết lập các cờ hủy trong bất cứ số miliseconds, tôi sẽ chỉ cần đặt nó như là một phần của callback:

myDependency.Stub(x => x.DoExpensiveStuff1()); 
myDependency.Stub(x => x.DoExpensiveStuff2()); 
myDependency.Stub(x => x.DoExpensiveStuff3()).WhenCalled(x => cts.Cancel()); 
myDependency.Stub(x => x.DoExpensiveStuff4()); 

từ quan điểm của mã của bạn này sẽ trông như thể hủy bỏ đã xảy ra trong suốt cuộc gọi.

+0

Điều này trông cực kỳ hứa hẹn. Tôi sẽ thử nó vào chiều nay, và sẽ báo cáo lại. –

+0

Nó hoạt động rất tốt. Cảm ơn bạn đã giúp đỡ! –

1

Mỗi hoạt động chạy dài sẽ kích hoạt sự kiện khi chúng bắt đầu chạy.

Đánh dấu sự kiện này trong bài kiểm tra đơn vị. Điều này mang lại kết quả xác định với tiềm năng rằng các sự kiện có thể hữu ích trong tương lai.