Cách giả lập ConfigurationManager.AppSettings với moq


123

Tôi bị mắc kẹt ở điểm mã này mà tôi không biết làm thế nào để mô phỏng:

ConfigurationManager.AppSettings["User"];

Tôi phải giả lập ConfigurationManager, nhưng tôi không có manh mối, tôi đang sử dụng Moq .

Ai đó có thể cho tôi một mẹo? Cảm ơn!

Câu trả lời:


104

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.


6
Đây là khái niệm những gì tôi đang làm. Tuy nhiên, tôi sử dụng Castle DictionaryAdapter (một phần của Castle Core) tạo ra việc triển khai giao diện một cách nhanh chóng. Tôi đã viết về nó một thời gian trước: blog.andreloker.de/post/2008/09/05/… (cuộn xuống "Giải pháp" để xem cách tôi sử dụng Castle DictionaryAdapter)
Andre Loker

Đó là điều tiện lợi và đó là một bài báo hay. Tôi sẽ phải ghi nhớ điều này cho tương lai.
Joshua Enfield

Tôi cũng có thể thêm - tùy thuộc vào cách hiểu và cách diễn giải của bạn - thay vào đó, điều này có thể được gọi là Proxy ủy quyền hoặc Bộ điều hợp.
Joshua Enfield

3
Từ phía trên, nó chỉ sử dụng Moq là "bình thường". Chưa được kiểm tra, nhưng một cái gì đó như: var configurationMock = new Mock<IConfiguration>();và để thiết lập:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
Joshua Enfield.

Kịch bản này được sử dụng khi một lớp phụ thuộc vào cấu hình IC và bạn cần mô phỏng cấu hình IC, nhưng bạn sẽ kiểm tra việc triển khai cấu hình IC như thế nào? Và nếu bạn gọi trong Unit Test ConfigurationManager.AppSettings ["Người dùng"] thì điều này sẽ không kiểm tra đơn vị, nhưng sẽ kiểm tra giá trị nào được tìm nạp từ tệp cấu hình, không phải là kiểm tra đơn vị. Nếu bạn cần kiểm tra việc triển khai, hãy xem @ zpbappi.com/testing-codes-with-configurationmanager-appsettings
nkalfov,

173

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.confighoặc myapp.config. ConfigurationsManagercó 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.


8
Đây thực sự là cách thông minh và đơn giản để giải quyết vấn đề! Kudos vì sự đơn giản!
Navap

1
Dễ dàng hơn nhiều so với việc tạo ra một sự trừu tượng trong nhiều trường hợp
Michael Clark

2
Đó là nó???? Sự sáng chói nằm ở sự đơn giản khi tôi đang vắt óc tìm cách kiểm tra lớp niêm phong đặc biệt này.
Piotr Kula,

5
ConfigurationManager.AppSettingskhông phải là một NameValueCollectionchuỗ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.
Ohad Schneider

1
Đơn giản và ngắn gọn. Cho đến nay câu trả lời tốt nhất!
znn

21

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".


7
Nếu hành vi của mã được kiểm tra thay đổi tùy thuộc vào giá trị của giá trị cấu hình, thì chắc chắn sẽ dễ dàng hơn để kiểm tra nó nếu nó không phụ thuộc trực tiếp vào AppSettings.
Andre Loker

2
Đây là một thực tiễn không tốt vì bạn không bao giờ thử nghiệm các cài đặt khả thi khác. Câu trả lời của Joshua Enfield là rất tốt để thử nghiệm.
mkaj

4
Trong khi những người khác phản đối câu trả lời này, tôi muốn nói rằng lập trường của họ có một chút khái quát. Đây là một câu trả lời rất hợp lệ trong một số trường hợp và nó thực sự chỉ phụ thuộc vào nhu cầu của bạn. Ví dụ: giả sử tôi có 4 cụm khác nhau, mỗi cụm có một URL cơ sở khác nhau. 4 cụm đó được kéo, trong thời gian chạy, từ Web.configdự á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.configlà 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.
Mike Perrenoud

14

Bạn có thể sử dụng miếng chêm để sửa đổi AppSettingsthà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.


6
Tác giả đang yêu cầu một cách với moq, không phải về MS Fakes.
JPCF

6
Và điều này khác nhau như thế nào? Nó đạt được sự chế nhạo bằng cách loại bỏ sự phụ thuộc dữ liệu khỏi mã của anh ta. Sử dụng C # Fakes là một trong những cách tiếp cận!
Zorayr

9

Bạn đã cân nhắc việc ăn cắp vặt thay vì chế giễu chưa? Các AppSettingsbấ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.


3
Tôi thích cách tiếp cận này nhất. Mặt khác, việc bao bọc trình quản lý cấu hình bằng IConfigurationnhư 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.AppSettingstrự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ì NameValueConnectionkhông an toàn cho luồng).
Ohad Schneider

2

Đó 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.


1

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.confignhư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; } }
}

1

Làm thế nào về việc chỉ thiết lập những gì bạn cần? Bởi vì, tôi không muốn chế nhạo .NET, tôi có ...?

System.Configuration.ConfigurationManager.AppSettings["myKey"] = "myVal";

Bạn có thể nên xóa AppSettings trước để đảm bảo rằng ứng dụng chỉ nhìn thấy những gì bạn muốn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.