Thực hành tốt nhất cho các phương pháp kiểm tra đơn vị sử dụng bộ đệm nhiều?


17

Tôi có một số phương thức logic nghiệp vụ lưu trữ và truy xuất (có lọc) các đối tượng và danh sách các đối tượng từ bộ đệm.

Xem xét

IList<TObject> AllFromCache() { ... }

TObject FetchById(guid id) { ... }

IList<TObject> FilterByPropertry(int property) { ... }

Fetch..Filter..sẽ gọi AllFromCachecái nào sẽ điền vào bộ đệm và trả về nếu nó không ở đó và chỉ trả về từ nó nếu có.

Tôi thường né tránh thử nghiệm đơn vị này. Thực hành tốt nhất để kiểm tra đơn vị đối với loại cấu trúc này là gì?

Tôi đã xem xét việc lưu trữ bộ đệm trên TestInitialize và xóa trên TestCleanup nhưng điều đó không phù hợp với tôi, (nó cũng có thể như vậy).

Câu trả lời:


18

Nếu bạn muốn Kiểm tra đơn vị thực sự, thì bạn phải giả lập bộ đệm: viết một đối tượng giả thực hiện giao diện giống như bộ đệm, nhưng thay vì là bộ đệm, nó sẽ theo dõi các cuộc gọi mà nó nhận được và luôn trả về những gì thực bộ nhớ cache sẽ được trả lại theo trường hợp thử nghiệm.

Tất nhiên, bộ nhớ cache cũng cần kiểm tra đơn vị sau đó, mà bạn phải giả định bất cứ điều gì nó phụ thuộc, v.v.

Những gì bạn mô tả, sử dụng đối tượng bộ đệm thực sự nhưng khởi tạo nó sang trạng thái đã biết và dọn dẹp sau khi kiểm tra, giống như một thử nghiệm tích hợp, bởi vì bạn đang thử nghiệm nhiều đơn vị trong buổi hòa nhạc.


+1 đây là cách tiếp cận tốt nhất. Kiểm tra đơn vị để kiểm tra logic sau đó kiểm tra tích hợp để thực sự xác minh bộ đệm hoạt động như bạn mong đợi.
Tom Squires

10

Các đơn Trách nhiệm Nguyên tắc là bạn thân nhất của mình tại đây.

Trước hết, di chuyển AllFromCache () vào một lớp kho lưu trữ và gọi nó là GetAll (). Rằng nó lấy từ bộ đệm là một chi tiết triển khai của kho lưu trữ và không được biết bởi mã gọi.

Điều này làm cho việc kiểm tra lớp lọc của bạn đẹp và dễ dàng. Nó không còn quan tâm nơi bạn nhận được nó từ.

Thứ hai, bọc lớp lấy dữ liệu từ cơ sở dữ liệu (hoặc bất cứ nơi nào) trong một trình bao bọc bộ đệm.

AOP là một kỹ thuật tốt cho việc này. Đó là một trong số ít những điều mà nó rất giỏi.

Sử dụng các công cụ như PostSharp , bạn có thể thiết lập nó để mọi phương thức được đánh dấu bằng thuộc tính được chọn sẽ được lưu vào bộ đệm. Tuy nhiên, nếu đây là điều duy nhất bạn lưu vào bộ nhớ cache, bạn không cần phải đi xa đến mức có khung AOP. Chỉ cần có một Kho lưu trữ và Bộ đệm ẩn sử dụng cùng một giao diện và đưa nó vào lớp gọi.

ví dụ.

public class ProductManager
{
    private IProductRepository ProductRepository { get; set; }

    public ProductManager
    {
        ProductRepository = productRepository;
    }

    Product FetchById(guid id) { ... }

    IList<Product> FilterByPropertry(int property) { ... }
}

public interface IProductRepository
{
    IList<Product> GetAll();
}

public class SqlProductRepository : IProductRepository
{
    public IList<Product> GetAll()
    {
        // DB Connection, fetch
    }
}

public class CachedProductRepository : IProductRepository
{
    private IProductRepository ProductRepository { get; set; }

    public CachedProductRepository (IProductRepository productRepository)
    {
        ProductRepository = productRepository;
    }

    public IList<Product> GetAll()
    {
        // Check cache, if exists then return, 
        // if not then call GetAll() on inner repository
    }
}

Xem cách bạn đã xóa kiến ​​thức triển khai kho lưu trữ khỏi ProductManager? Xem thêm cách bạn đã tuân thủ Nguyên tắc Trách nhiệm Đơn lẻ bằng cách có một lớp xử lý trích xuất dữ liệu, một lớp xử lý truy xuất dữ liệu và một lớp xử lý bộ đệm ẩn?

Bây giờ bạn có thể khởi tạo Trình quản lý sản phẩm với một trong các Kho lưu trữ đó và nhận bộ đệm ẩn ... hoặc không. Điều này cực kỳ hữu ích sau này khi bạn gặp một lỗi khó hiểu mà bạn nghi ngờ là kết quả của bộ đệm.

productManager = new ProductManager(
                         new SqlProductRepository()
                         );

productManager = new ProductManager(
                         new CachedProductRepository(new SqlProductRepository())
                         );

(Nếu bạn đang sử dụng bộ chứa IOC, thậm chí còn tốt hơn. Rõ ràng là làm thế nào để thích nghi.)

Và, trong các bài kiểm tra ProductManager của bạn

IProductRepository repo = MockRepository.GenerateStrictMock<IProductRepository>();

Không cần phải kiểm tra bộ đệm.

Bây giờ câu hỏi trở thành: Tôi có nên kiểm tra Bộ nhớ đệm đó không? Tôi đề nghị không. Bộ nhớ cache khá không xác định. Khung làm việc với nó nằm ngoài tầm kiểm soát của bạn. Giống như, chỉ cần loại bỏ những thứ từ nó khi nó quá đầy, chẳng hạn. Bạn sẽ kết thúc với các bài kiểm tra thất bại một lần trong một mặt trăng xanh và bạn sẽ không bao giờ thực sự hiểu tại sao.

Và, đã thực hiện các thay đổi mà tôi đã đề xuất ở trên, thực sự không có nhiều logic để kiểm tra trong đó. Thử nghiệm thực sự quan trọng, phương thức lọc, sẽ ở đó và được trừu tượng hóa hoàn toàn từ chi tiết của GetAll (). GetAll () chỉ ... có tất cả. Từ nơi nào đó.


Bạn sẽ làm gì nếu bạn đang sử dụng Bộ nhớ đệm Sản phẩm trong Trình quản lý sản phẩm nhưng muốn sử dụng các phương thức có trong SQLSản phẩm lưu trữ?
Jonathan

@Jonathan: "Chỉ cần có Kho lưu trữ và Bộ đệm ẩn sử dụng cùng một giao diện" - nếu chúng có cùng giao diện, bạn có thể sử dụng các phương thức tương tự. Mã gọi không cần biết gì về triển khai.
pdr

3

Cách tiếp cận được đề xuất của bạn là những gì tôi sẽ làm. Đưa ra mô tả của bạn, kết quả của phương thức sẽ giống nhau cho dù đối tượng có mặt trong bộ đệm hay không: bạn vẫn sẽ nhận được kết quả tương tự. Thật dễ dàng để kiểm tra bằng cách thiết lập bộ đệm theo một cách cụ thể trước mỗi lần kiểm tra. Có thể có một số trường hợp bổ sung như nếu hướng dẫn là nullhoặc không có đối tượng có thuộc tính được yêu cầu; những người có thể được kiểm tra quá.

Ngoài ra, bạn có thể xem xét rằng đối tượng sẽ có mặt trong bộ đệm sau khi phương thức của bạn trở lại, bất kể nó có ở trong bộ đệm ở vị trí đầu tiên hay không. Điều này gây tranh cãi, vì một số người (bao gồm cả tôi) sẽ tranh luận rằng bạn quan tâm đến những gì bạn nhận được từ giao diện của mình chứ không phải cách bạn nhận được nó (tức là thử nghiệm của bạn rằng giao diện hoạt động như mong đợi, không phải là nó có triển khai cụ thể). Nếu bạn coi nó là quan trọng, bạn có cơ hội để kiểm tra điều đó.


1

Tôi đã xem xét việc lưu trữ bộ đệm trên TestInitialize và xóa trên TestCleanup nhưng điều đó không phù hợp với tôi

Trên thực tế, đó là cách duy nhất đúng để làm. Đó là những gì hai chức năng đó có: để thiết lập các điều kiện tiên quyết và dọn dẹp. Nếu các điều kiện tiên quyết không được đáp ứng, chương trình của bạn có thể không hoạt động.


0

Tôi đã làm việc trên một số thử nghiệm sử dụng bộ đệm gần đây. Tôi đã tạo một trình bao bọc xung quanh lớp hoạt động với bộ đệm, và sau đó có xác nhận rằng trình bao bọc này đang được gọi.

Tôi đã làm điều này chủ yếu bởi vì lớp hiện có hoạt động với bộ đệm là tĩnh.


0

Có vẻ như bạn muốn kiểm tra logic bộ đệm, nhưng không phải logic điền. Vì vậy, tôi sẽ đề nghị bạn chế giễu những gì bạn không cần kiểm tra - dân cư.

AllFromCache()Phương pháp của bạn đảm nhiệm việc điền vào bộ đệm và điều đó nên được ủy quyền cho một thứ khác, như nhà cung cấp các giá trị. Vì vậy, mã của bạn sẽ trông như thế nào

private Supplier<TObject> supplier;

IList<TObject> AllFromCache() {
    if (!cacheInitialized) {
        //whatever logic needed to fill the cache
        cache.putAll(supplier.getValues());
        cacheInitialized = true;
    }

    return  cache.getAll();
}

Bây giờ bạn có thể giả định nhà cung cấp để thử nghiệm, để trả về một số giá trị được xác định trước. Bằng cách đó, bạn có thể kiểm tra bộ lọc và tìm nạp thực tế của mình và không tải các đối tượng.

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.