2010-01-18 50 views
39

Tôi đã chuẩn bị một số thử nghiệm tự động với khung kiểm thử Visual Studio Team Edition. Tôi muốn một trong các bài kiểm tra kết nối với cơ sở dữ liệu theo cách bình thường được thực hiện trong chương trình:Thử nghiệm đơn vị với số đơn

string r_providerName = ConfigurationManager.ConnectionStrings["main_db"].ProviderName; 

Nhưng tôi nhận được ngoại lệ trong dòng này. Tôi cho rằng điều này đang xảy ra bởi vì ConfigurationManager là một singleton. Làm thế nào bạn có thể làm việc xung quanh vấn đề singleton với các bài kiểm tra đơn vị?


Cảm ơn bạn đã trả lời. Tất cả chúng đều rất có tính hướng dẫn.

+1

Thông báo lỗi chính xác là gì? – Kane

+0

Null pointer exception – yeyeyerman

Trả lời

69
+1

Tất cả những tài liệu tham khảo giải quyết vấn đề sâu sắc hơn mà tôi có thể tóm tắt trong một câu trả lời. Họ chắc chắn là tuyệt vời. –

+2

+1! Xem thêm cuốn sách "Làm việc hiệu quả với Bộ luật di sản" của Michael Feathers; anh ấy cung cấp các kỹ thuật để thử nghiệm với Singletons. http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052 – TrueWill

+0

Đặc biệt thú vị là liên kết * Performant Singletons *, dẫn đến trang lỗi ** 403 Forbidden **. Khá một cách tinh tế để thể hiện sự từ chối. – derM

13

Bạn có thể sử dụng tiêm phụ thuộc hàm tạo. Ví dụ:

public class SingletonDependedClass 
{ 
    private string _ProviderName; 

    public SingletonDependedClass() 
     : this(ConfigurationManager.ConnectionStrings["main_db"].ProviderName) 
    { 
    } 

    public SingletonDependedClass(string providerName) 
    { 
     _ProviderName = providerName; 
    } 
} 

Điều đó cho phép bạn chuyển chuỗi kết nối trực tiếp đến đối tượng trong khi thử nghiệm.

Ngoài ra nếu bạn sử dụng khung kiểm thử Visual Studio Team Edition, bạn có thể tạo hàm tạo với tham số riêng và kiểm tra lớp thông qua trình truy cập.

Thực ra tôi giải quyết vấn đề đó với chế nhạo. Ví dụ:

Bạn có một lớp mà phụ thuộc vào singleton:

public class Singleton 
{ 
    public virtual string SomeProperty { get; set; } 

    private static Singleton _Instance; 
    public static Singleton Insatnce 
    { 
     get 
     { 
      if (_Instance == null) 
      { 
       _Instance = new Singleton(); 
      } 

      return _Instance; 
     } 
    } 

    protected Singleton() 
    { 
    } 
} 

public class SingletonDependedClass 
{ 
    public void SomeMethod() 
    { 
     ... 
     string str = Singleton.Insatnce.SomeProperty; 
     ... 
    } 
} 

Trước hết SingletonDependedClass cần phải được refactored để có Singleton dụ như tham số constructor:

public class SingletonDependedClass 
{  
    private Singleton _SingletonInstance; 

    public SingletonDependedClass() 
     : this(Singleton.Insatnce) 
    { 
    } 

    private SingletonDependedClass(Singleton singletonInstance) 
    { 
     _SingletonInstance = singletonInstance; 
    } 

    public void SomeMethod() 
    { 
     string str = _SingletonInstance.SomeProperty; 
    } 
} 

Test of SingletonDependedClass (Moq mocking library được sử dụng):

[TestMethod()] 
public void SomeMethodTest() 
{ 
    var singletonMock = new Mock<Singleton>(); 
    singletonMock.Setup(s => s.SomeProperty).Returns("some test data"); 
    var target = new SingletonDependedClass_Accessor(singletonMock.Object); 
    ... 
} 
5

Bạn đang đối mặt với một vấn đề chung hơn ở đây. Nếu bị lạm dụng, Singletons cản trở việc kiểm tra.

Tôi đã thực hiện một detailed analysis của sự cố này trong bối cảnh thiết kế được tách rời.Tôi sẽ cố gắng tóm tắt các điểm của mình:

  1. Nếu Singleton của bạn mang trạng thái toàn cầu quan trọng, không sử dụng Singleton. Điều này bao gồm lưu trữ liên tục như Cơ sở dữ liệu, Tệp, v.v.
  2. Trong trường hợp, phụ thuộc vào đối tượng Singleton không rõ ràng theo tên lớp, phụ thuộc phải được tiêm. Sự cần thiết phải đưa Singleton Instances vào các lớp chứng tỏ việc sử dụng sai mẫu (xem điểm 1).
  3. Vòng đời của Singleton được giả định giống như của ứng dụng. Hầu hết các triển khai Singleton đều sử dụng cơ chế tải chậm để tự khởi tạo. Điều này là tầm thường và vòng đời của họ không có khả năng thay đổi, nếu không bạn không nên sử dụng Singleton.
7

Ví dụ từ Sổ: Working Effectively with Legacy Code

Cũng cho cùng một câu trả lời ở đây: https://stackoverflow.com/a/28613595/929902

Để chạy mã chứa độc thân trong một khai thác thử nghiệm, chúng ta phải nghỉ ngơi tài sản singleton. Đây là cách chúng tôi làm điều đó. Bước đầu tiên là thêm một phương thức tĩnh mới vào lớp singleton. Phương thức này cho phép chúng ta thay thế thể hiện tĩnh trong singleton. Chúng tôi sẽ gọi nó là setTestingInstance.

public class PermitRepository 
{ 
    private static PermitRepository instance = null; 
    private PermitRepository() {} 
    public static void setTestingInstance(PermitRepository newInstance) 
    { 
     instance = newInstance; 
    } 
    public static PermitRepository getInstance() 
    { 
     if (instance == null) { 
      instance = new PermitRepository(); 
     } 
     return instance; 
    } 
    public Permit findAssociatedPermit(PermitNotice notice) { 
    ... 
    } 
    ... 
} 

Bây giờ chúng ta có setter đó, chúng ta có thể tạo một trường hợp thử nghiệm của PermitRepository và đặt nó. Chúng tôi muốn viết mã như thế này trong thiết lập thử nghiệm của chúng tôi:

public void setUp() { 
    PermitRepository repository = PermitRepository.getInstance(); 
    ... 
    // add permits to the repository here 
    ... 
    PermitRepository.setTestingInstance(repository); 
} 
+4

Dường như có điều gì đó bị thiếu ở đây trong phương thức setUp(). PermitRepository có một hàm tạo riêng, vì vậy bạn không thể sử dụng mới ở đó ... – leogtzr

+0

Trong phương thức thiết lập của bạn, bạn đang gọi lấy cá thể trước khi thiết lập cá thể kiểm thử của bạn. Bạn muốn đặt thể hiện thử nghiệm của mình trước tiên. –