Câu trả lời:
Tôi tin rằng một cách tiếp cận tiêu chuẩn cho điều này là sử dụng một mẫu mặt tiền để bao bọc trình quản lý cấu hình và sau đó bạn có một thứ gì đó được kết hợp lỏng lẻo mà bạn có quyền kiểm soát.
Vì vậy, bạn sẽ bao bọc ConfigurationManager. Cái gì đó như:
public class Configuration: IConfiguration
{
public User
{
get
{
return ConfigurationManager.AppSettings["User"];
}
}
}
(Bạn chỉ có thể trích xuất một giao diện từ lớp cấu hình của mình và sau đó sử dụng giao diện đó ở mọi nơi trong mã của bạn) Sau đó, bạn chỉ cần giả lập cấu hình ICon. Bạn có thể tự triển khai mặt tiền theo một số cách khác nhau. Ở trên, tôi chỉ chọn gói các thuộc tính riêng lẻ. Bạn cũng có được lợi ích phụ của việc thông tin được nhập mạnh để làm việc với các mảng băm được nhập yếu.
var configurationMock = new Mock<IConfiguration>();
và để thiết lập:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
Tôi đang sử dụng AspnetMvc4. Một lúc trước tôi đã viết
ConfigurationManager.AppSettings["mykey"] = "myvalue";
trong phương pháp thử nghiệm của tôi và nó hoạt động hoàn hảo.
Giải thích: phương pháp thử nghiệm chạy trong ngữ cảnh với cài đặt ứng dụng được lấy từ, thường là web.config
hoặc myapp.config
. ConfigurationsManager
có thể tiếp cận đối tượng ứng dụng-toàn cục này và thao tác với nó.
Mặc dù: Nếu bạn có một người chạy thử nghiệm chạy các bài kiểm tra song song thì đây không phải là một ý kiến hay.
ConfigurationManager.AppSettings
không phải là một NameValueCollection
chuỗi an toàn, vì vậy các thử nghiệm song song bằng cách sử dụng nó mà không có đồng bộ hóa thích hợp dù sao cũng không phải là một ý kiến hay. Nếu không, bạn có thể chỉ cần gọi ConfigurationManager.AppSettings.Clear()
trong TestInitialize
/ ctor của bạn và bạn là vàng.
Có thể không phải là điều bạn cần hoàn thành, nhưng bạn đã cân nhắc sử dụng app.config trong dự án thử nghiệm của mình chưa? Vì vậy, ConfigurationManager sẽ nhận các giá trị mà bạn đặt trong app.config và bạn không cần phải giả mạo bất cứ thứ gì. Giải pháp này hoạt động tốt cho nhu cầu của tôi, vì tôi không bao giờ cần kiểm tra tệp cấu hình "biến".
Web.config
dự án bao trùm. Trong quá trình thử nghiệm, việc lấy một số giá trị đã biết từ giá trị app.config
là rất hợp lệ. Bài kiểm tra đơn vị chỉ cần đảm bảo các điều kiện về khi nó kéo "cluster1" hoạt động; chỉ có 4 cụm khác nhau trong trường hợp này.
Bạn có thể sử dụng miếng chêm để sửa đổi AppSettings
thành một NameValueCollection
đối tượng tùy chỉnh . Đây là một ví dụ về cách bạn có thể đạt được điều này:
[TestMethod]
public void TestSomething()
{
using(ShimsContext.Create()) {
const string key = "key";
const string value = "value";
ShimConfigurationManager.AppSettingsGet = () =>
{
NameValueCollection nameValueCollection = new NameValueCollection();
nameValueCollection.Add(key, value);
return nameValueCollection;
};
///
// Test code here.
///
// Validation code goes here.
}
}
Bạn có thể đọc thêm về hàng giả và hàng giả tại, Cách ly Mã đang Kiểm tra với Hàng giả của Microsoft . Hi vọng điêu nay co ich.
Bạn đã cân nhắc việc ăn cắp vặt thay vì chế giễu chưa? Các AppSettings
bất động sản là một NameValueCollection
:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
// Arrange
var settings = new NameValueCollection {{"User", "Otuyh"}};
var classUnderTest = new ClassUnderTest(settings);
// Act
classUnderTest.MethodUnderTest();
// Assert something...
}
}
public class ClassUnderTest
{
private readonly NameValueCollection _settings;
public ClassUnderTest(NameValueCollection settings)
{
_settings = settings;
}
public void MethodUnderTest()
{
// get the User from Settings
string user = _settings["User"];
// log
Trace.TraceInformation("User = \"{0}\"", user);
// do something else...
}
}
Lợi ích là việc triển khai đơn giản hơn và không phụ thuộc vào System.Configuration cho đến khi bạn thực sự cần.
IConfiguration
như Joshua Enfield đề xuất có thể là mức quá cao và bạn có thể bỏ lỡ các lỗi tồn tại do những thứ như phân tích cú pháp giá trị cấu hình kém. Mặt khác, sử dụng ConfigurationManager.AppSettings
trực tiếp như LosManos gợi ý là quá nhiều chi tiết thực hiện, chưa kể nó có thể có tác dụng phụ đối với các thử nghiệm khác và không thể sử dụng trong các lần chạy thử nghiệm song song mà không đồng bộ hóa thủ công (vì NameValueConnection
không an toàn cho luồng).
Đó là thuộc tính tĩnh và Moq được thiết kế cho các phương thức hoặc lớp thể hiện của Moq có thể được giả mạo thông qua kế thừa. Nói cách khác, Moq sẽ không giúp được gì cho bạn ở đây.
Để bắt chước tĩnh, tôi sử dụng một công cụ có tên là Moles , miễn phí. Có những công cụ cách ly khuôn khổ khác, như Typemock cũng có thể làm điều này, mặc dù tôi tin rằng đó là những công cụ trả phí.
Khi nói đến tĩnh và thử nghiệm, một tùy chọn khác là tự tạo trạng thái tĩnh, mặc dù điều này thường có thể có vấn đề (như tôi nghĩ sẽ xảy ra trong trường hợp của bạn).
Và, cuối cùng, nếu các khuôn khổ cô lập không phải là một lựa chọn và bạn cam kết với cách tiếp cận này, thì mặt tiền mà Joshua đề cập là một cách tiếp cận tốt hoặc bất kỳ cách tiếp cận nào nói chung khi bạn coi mã khách hàng của điều này ra khỏi logic kinh doanh mà bạn đang sử dụng để kiểm tra.
Tôi nghĩ rằng viết cho bạn nhà cung cấp app.config của riêng bạn là một nhiệm vụ đơn giản và hữu ích hơn bất cứ điều gì khác. Đặc biệt là bạn nên tránh bất kỳ hàng giả nào như miếng chêm, v.v. bởi vì ngay sau khi bạn sử dụng chúng, Chỉnh sửa & Tiếp tục không còn hoạt động nữa.
Các nhà cung cấp mà tôi sử dụng trông như thế này:
Theo mặc định, chúng nhận các giá trị từ App.config
nhưng đối với các bài kiểm tra đơn vị, tôi có thể ghi đè tất cả các giá trị và sử dụng chúng trong mỗi bài kiểm tra một cách độc lập.
Không cần bất kỳ giao diện nào hoặc triển khai nó lặp đi lặp lại. Tôi có một dll tiện ích và sử dụng trình trợ giúp nhỏ này trong nhiều dự án và thử nghiệm đơn vị.
public class AppConfigProvider
{
public AppConfigProvider()
{
ConnectionStrings = new ConnectionStringsProvider();
AppSettings = new AppSettingsProvider();
}
public ConnectionStringsProvider ConnectionStrings { get; private set; }
public AppSettingsProvider AppSettings { get; private set; }
}
public class ConnectionStringsProvider
{
private readonly Dictionary<string, string> _customValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public string this[string key]
{
get
{
string customValue;
if (_customValues.TryGetValue(key, out customValue))
{
return customValue;
}
var connectionStringSettings = ConfigurationManager.ConnectionStrings[key];
return connectionStringSettings == null ? null : connectionStringSettings.ConnectionString;
}
}
public Dictionary<string, string> CustomValues { get { return _customValues; } }
}
public class AppSettingsProvider
{
private readonly Dictionary<string, string> _customValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public string this[string key]
{
get
{
string customValue;
return _customValues.TryGetValue(key, out customValue) ? customValue : ConfigurationManager.AppSettings[key];
}
}
public Dictionary<string, string> CustomValues { get { return _customValues; } }
}