2008-12-11 26 views
7

Vì vậy, tôi là người mới dùng TDD và tôi đã tạo thành công một ứng dụng mẫu nhỏ đẹp bằng mẫu MVP. Vấn đề lớn đối với giải pháp hiện tại của tôi là nó chặn luồng UI, Vì vậy, tôi đã cố gắng để thiết lập Presenter để sử dụng SynchronizationContext.Current, nhưng khi tôi chạy thử nghiệm của tôi SynchronizationContext.Current là null.Tái cấu trúc thử nghiệm TDD để hỗ trợ đa luồng

Presenter Trước Threading

public class FtpPresenter : IFtpPresenter 
{ 
    ... 
    void _view_GetFilesClicked(object sender, EventArgs e) 
    { 
     _view.StatusMessage = Messages.Loading; 

     try 
     { 
      var settings = new FtpAuthenticationSettings() 
      { 
       Site = _view.FtpSite, 
       Username = _view.FtpUsername, 
       Password = _view.FtpPassword 
      }; 
      var files = _ftpService.GetFiles(settings); 

      _view.FilesDataSource = files; 
      _view.StatusMessage = Messages.Done;   
     } 
     catch (Exception ex) 
     { 
      _view.StatusMessage = ex.Message; 
     } 
    } 
    ... 
} 

Kiểm tra Trước khi Threading

[TestMethod] 
public void Can_Get_Files() 
{ 
    var view = new FakeFtpView(); 
    var presenter = new FtpPresenter(view, new FakeFtpService(), new FakeFileValidator()); 

    view.GetFiles(); 
    Assert.AreEqual(Messages.Done, view.StatusMessage); 
} 

Bây giờ sau khi tôi thêm một SynchronizationContext Threading cho Presenter tôi đã cố gắng để thiết lập một AutoResetEvent trên Xem Fake tôi cho StatusMessage, nhưng khi Tôi chạy thử nghiệm SynchronizationContext.Current là null. Tôi nhận ra rằng các mô hình luồng tôi đang sử dụng trong thuyết trình mới của tôi không phải là hoàn hảo, nhưng đây có phải là kỹ thuật đúng cho kiểm tra đa luồng? Tại sao là SynchronizationContext.Current null của tôi? Tôi nên làm gì?

Presenter Sau Threading

public class FtpPresenter : IFtpPresenter 
{ 
    ... 
    void _view_GetFilesClicked(object sender, EventArgs e) 
    { 
     _view.StatusMessage = Messages.Loading; 

     try 
     { 
      var settings = new FtpAuthenticationSettings() 
      { 
       Site = _view.FtpSite, 
       Username = _view.FtpUsername, 
       Password = _view.FtpPassword 
      }; 
      // Wrap the GetFiles in a ThreadStart 
      var syncContext = SynchronizationContext.Current; 
      new Thread(new ThreadStart(delegate 
      { 
       var files = _ftpService.GetFiles(settings); 
       syncContext.Send(delegate 
       { 
        _view.FilesDataSource = files; 
        _view.StatusMessage = Messages.Done; 
       }, null); 
      })).Start(); 
     } 
     catch (Exception ex) 
     { 
      _view.StatusMessage = ex.Message; 
     } 
    } 
    ... 
} 

thử nghiệm sau khi luồng

[TestMethod] 
public void Can_Get_Files() 
{ 
    var view = new FakeFtpView(); 
    var presenter = new FtpPresenter(view, new FakeFtpService(), new FakeFileValidator()); 

    view.GetFiles(); 
    view.GetFilesWait.WaitOne(); 
    Assert.AreEqual(Messages.Done, view.StatusMessage); 
} 

Fake Xem

public class FakeFtpView : IFtpView 
{ 
    ... 
    public AutoResetEvent GetFilesWait = new AutoResetEvent(false); 
    public event EventHandler GetFilesClicked = delegate { }; 
    public void GetFiles() 
    { 
     GetFilesClicked(this, EventArgs.Empty); 
    } 
    ... 
    private List<string> _statusHistory = new List<string>(); 
    public List<string> StatusMessageHistory 
    { 
     get { return _statusHistory; } 
    } 
    public string StatusMessage 
    { 
     get 
     { 
      return _statusHistory.LastOrDefault(); 
     } 
     set 
     { 
      _statusHistory.Add(value); 
      if (value != Messages.Loading) 
       GetFilesWait.Set(); 
     } 
    } 
    ... 
} 
+0

Câu hỏi hay! Tôi đang cố giải quyết vấn đề tương tự! –

Trả lời

3

tôi đã chạy vào vấn đề tương tự với ASP.NET MVC nó ở đâu HttpContext đó là mất tích. Một điều bạn có thể làm là cung cấp một hàm tạo thay thế cho phép bạn tiêm một SynchronizationContext giả hoặc phơi bày một bộ công khai thực hiện điều tương tự. Nếu bạn không thể thay đổi SynchronizationContext nội bộ, sau đó tạo một thuộc tính mà bạn đã đặt thành SynchronizationContext.Current trong hàm dựng mặc định và sử dụng thuộc tính đó trong toàn bộ mã của bạn. Trong phương thức khởi tạo thay thế của bạn, bạn có thể gán ngữ cảnh giả cho thuộc tính - hoặc bạn có thể gán trực tiếp cho nó nếu bạn cho nó một bộ công khai.

lớp công khai FtpPresenter: IFtpPresenter { Đồng bộ hóa công cộngContext CurrentContext {get; bộ; }

public FtpPresenter() : this(null) { } 

    public FtpPresenter(SynchronizationContext context) 
    { 
     this.CurrentContext = context ?? SynchronizationContext.Current; 
    } 

    void _view_GetFilesClicked(object sender, EventArgs e) 
    { 
    .... 
    new Thread(new ThreadStart(delegate 
     { 
      var files = _ftpService.GetFiles(settings); 
      this.CurrentContext.Send(delegate 
      { 
       _view.FilesDataSource = files; 
       _view.StatusMessage = Messages.Done; 
      }, null); 
     })).Start(); 

    ... 
    } 

Một quan sát khác mà tôi sẽ thực hiện là tôi có thể có người trình bày của bạn phụ thuộc vào giao diện cho lớp Thread thay vì trực tiếp trên Thread. Tôi không nghĩ rằng các bài kiểm tra đơn vị của bạn nên tạo các chủ đề mới mà là tương tác với một lớp mô hình mà chỉ đảm bảo rằng các phương thức thích hợp để tạo các luồng được gọi. Bạn có thể tiêm sự phụ thuộc đó.

Nếu SynchronizationContext.Current không tồn tại khi hàm tạo được gọi, bạn có thể cần di chuyển logic gán vào hiện tại vào bộ nạp và tải xuống.

+0

Tôi sẽ sử dụng những gì trong bài kiểm tra của mình thay thế cho SynchronizationContext.Current? Bạn có mẫu mã nào không? – bendewey

+0

Tôi không biết liệu Synchronizationcontext có phù hợp với giao diện hay không. Nếu vậy, bạn có thể giả lập một lớp có sử dụng cùng một giao diện và tiêm giao diện thay thế. Nếu không, bạn có thể định nghĩa một trình bao bọc xung quanh SynchronizationContext thực hiện giao diện (của riêng bạn) và giả lập lớp trình bao bọc. – tvanfosson

+0

Nhìn vào nguồn của HttpContextWrapper (đó là những gì tôi quen thuộc) tại www.codeplex.com/aspnet trong cây nguồn MVC để có ý tưởng về cách thực hiện điều này. – tvanfosson

1

Bạn có nhiều ứng dụng logic trong trình bày của bạn. Tôi sẽ ẩn các bối cảnh và chủ đề bên trong một mô hình cụ thể và kiểm tra chức năng một mình.

Các vấn đề liên quan