Bạn đang thử nghiệm đơn vị với Entity Framework 6 như thế nào?


170

Tôi chỉ mới bắt đầu với bài kiểm tra Đơn vị và TDD nói chung. Tôi đã từng học hỏi trước đây nhưng bây giờ tôi quyết tâm thêm nó vào quy trình làm việc của mình và viết phần mềm tốt hơn.

Tôi đã hỏi một câu hỏi ngày hôm qua bao gồm loại này, nhưng nó dường như là một câu hỏi riêng. Tôi đã ngồi xuống để bắt đầu triển khai một lớp dịch vụ mà tôi sẽ sử dụng để trừu tượng hóa logic nghiệp vụ khỏi các bộ điều khiển và ánh xạ tới các mô hình và tương tác dữ liệu cụ thể bằng cách sử dụng EF6.

Vấn đề là tôi đã tự chặn đường vì tôi không muốn trừu tượng hóa EF trong kho lưu trữ (nó vẫn sẽ có sẵn bên ngoài các dịch vụ cho các truy vấn cụ thể, v.v.) và muốn kiểm tra các dịch vụ của tôi (EF Context sẽ được sử dụng) .

Ở đây tôi đoán là câu hỏi, có một điểm để làm điều này? Nếu vậy, mọi người đang làm điều đó như thế nào trong bối cảnh trừu tượng bị rò rỉ do IQueryable gây ra và nhiều bài viết tuyệt vời của Ladislav Mrnka về chủ đề kiểm tra đơn vị không đơn giản vì sự khác biệt trong các nhà cung cấp Linq khi làm việc với bộ nhớ thực hiện như được áp dụng cho một cơ sở dữ liệu cụ thể.

Mã tôi muốn kiểm tra có vẻ khá đơn giản. (đây chỉ là mã giả để thử và hiểu những gì tôi đang làm, tôi muốn thúc đẩy việc tạo bằng TDD)

Bối cảnh

public interface IContext
{
    IDbSet<Product> Products { get; set; }
    IDbSet<Category> Categories { get; set; }
    int SaveChanges();
}

public class DataContext : DbContext, IContext
{
    public IDbSet<Product> Products { get; set; }
    public IDbSet<Category> Categories { get; set; }

    public DataContext(string connectionString)
                : base(connectionString)
    {

    }
}

Dịch vụ

public class ProductService : IProductService
{
    private IContext _context;

    public ProductService(IContext dbContext)
    {
        _context = dbContext;
    }

    public IEnumerable<Product> GetAll()
    {
        var query = from p in _context.Products
                    select p;

        return query;
    }
}

Hiện tại tôi đang trong suy nghĩ làm một vài điều:

  1. Mocking EF Bối cảnh với một cái gì đó giống như cách tiếp cận này - Mocking EF Khi Kiểm tra đơn vị hoặc trực tiếp sử dụng khung mô phỏng trên giao diện như moq - chịu nỗi đau mà các bài kiểm tra đơn vị có thể vượt qua nhưng không nhất thiết phải kết thúc và sao lưu chúng bằng các bài kiểm tra Tích hợp?
  2. Có lẽ sử dụng thứ gì đó như Effort để chế nhạo EF - Tôi chưa bao giờ sử dụng nó và không chắc có ai khác đang sử dụng nó trong tự nhiên không?
  3. Không bận tâm kiểm tra bất cứ điều gì chỉ đơn giản gọi lại cho EF - vì vậy về cơ bản các phương thức dịch vụ gọi trực tiếp cho EF (getAll, v.v.) không phải là đơn vị được thử nghiệm mà chỉ là thử nghiệm tích hợp?

Bất cứ ai ngoài đó thực sự làm điều này ra khỏi đó mà không có Repo và có thành công?


Này Modika, tôi đã suy nghĩ về điều này gần đây (vì câu hỏi này: stackoverflow.com/questions/25977388/ mẹo ) Trong đó tôi cố gắng mô tả chính thức hơn một chút về cách tôi làm việc vào lúc này, nhưng tôi rất thích nghe làm thế nào bạn đang làm nó
samy

Xin chào @samy, cách chúng tôi quyết định thực hiện không phải là đơn vị kiểm tra bất cứ thứ gì chạm trực tiếp vào EF. Các truy vấn đã được thử nghiệm nhưng là thử nghiệm tích hợp, không phải thử nghiệm đơn vị. Mocking EF cảm thấy hơi bẩn, nhưng dự án này là nhỏ vì vậy tác động hiệu suất của việc có vô số bài kiểm tra đánh vào cơ sở dữ liệu không thực sự đáng lo ngại nên chúng tôi có thể thực tế hơn một chút về nó. Tôi vẫn không chắc chắn 100% cách tiếp cận tốt nhất là hoàn toàn trung thực với bạn, đến một lúc nào đó bạn sẽ đánh vào EF (và DB của bạn) và thử nghiệm đơn vị không phù hợp với tôi ở đây.
Modika

Câu trả lời:


186

Đây là một chủ đề tôi rất quan tâm. Có nhiều người theo chủ nghĩa thuần túy nói rằng bạn không nên thử nghiệm các công nghệ như EF và NHibernate. Họ đã đúng, họ đã được kiểm tra rất nghiêm ngặt và như một câu trả lời trước đó đã nói rằng việc dành nhiều thời gian để kiểm tra những gì bạn không sở hữu là vô nghĩa.

Tuy nhiên, bạn sở hữu cơ sở dữ liệu bên dưới! Theo quan điểm của tôi, đây là cách tiếp cận bị phá vỡ, bạn không cần phải kiểm tra rằng EF / NH đang thực hiện công việc của họ một cách chính xác. Bạn cần kiểm tra rằng ánh xạ / triển khai của bạn đang hoạt động với cơ sở dữ liệu của bạn. Theo tôi đây là một trong những phần quan trọng nhất của hệ thống mà bạn có thể kiểm tra.

Nói đúng ra, tuy nhiên, chúng tôi đang chuyển ra khỏi lĩnh vực thử nghiệm đơn vị và sang thử nghiệm tích hợp nhưng các nguyên tắc vẫn giữ nguyên.

Điều đầu tiên bạn cần làm là có thể giả lập DAL của bạn để BLL của bạn có thể được kiểm tra độc lập với EF và SQL. Đây là những bài kiểm tra đơn vị của bạn. Tiếp theo, bạn cần thiết kế các Bài kiểm tra Tích hợp để chứng minh DAL của mình, theo tôi đây là những điều quan trọng nhất.

Có một vài điều cần xem xét:

  1. Cơ sở dữ liệu của bạn cần ở trạng thái đã biết với mỗi bài kiểm tra. Hầu hết các hệ thống sử dụng một bản sao lưu hoặc tạo tập lệnh cho việc này.
  2. Mỗi bài kiểm tra phải được lặp lại
  3. Mỗi bài kiểm tra phải là nguyên tử

Có hai cách tiếp cận chính để thiết lập cơ sở dữ liệu của bạn, đầu tiên là chạy tập lệnh DB tạo đơn vị. Điều này đảm bảo rằng cơ sở dữ liệu kiểm tra đơn vị của bạn sẽ luôn ở trạng thái giống nhau ở đầu mỗi thử nghiệm (bạn có thể đặt lại điều này hoặc chạy từng thử nghiệm trong một giao dịch để đảm bảo điều này).

Tùy chọn khác của bạn là những gì tôi làm, chạy các thiết lập cụ thể cho từng thử nghiệm riêng lẻ. Tôi tin rằng đây là cách tiếp cận tốt nhất vì hai lý do chính:

  • Cơ sở dữ liệu của bạn đơn giản hơn, bạn không cần toàn bộ lược đồ cho mỗi bài kiểm tra
  • Mỗi thử nghiệm sẽ an toàn hơn, nếu bạn thay đổi một giá trị trong tập lệnh tạo của mình thì nó không làm mất hiệu lực hàng chục thử nghiệm khác.

Thật không may, sự thỏa hiệp của bạn ở đây là tốc độ. Phải mất thời gian để chạy tất cả các thử nghiệm này, để chạy tất cả các tập lệnh thiết lập / phá bỏ này.

Một điểm cuối cùng, có thể rất khó để viết một lượng SQL lớn như vậy để kiểm tra ORM của bạn. Đây là nơi tôi thực hiện một cách tiếp cận rất khó chịu (những người theo chủ nghĩa thuần túy ở đây sẽ không đồng ý với tôi). Tôi sử dụng ORM của mình để tạo bài kiểm tra của mình! Thay vì có một tập lệnh riêng cho mọi thử nghiệm DAL trong hệ thống của tôi, tôi có giai đoạn thiết lập thử nghiệm để tạo các đối tượng, gắn chúng vào ngữ cảnh và lưu chúng. Sau đó tôi chạy thử nghiệm của tôi.

Điều này khác xa với giải pháp lý tưởng tuy nhiên trong thực tế tôi thấy nó rất dễ quản lý (đặc biệt là khi bạn có vài nghìn bài kiểm tra), nếu không, bạn đang tạo ra số lượng lớn các tập lệnh. Thực tiễn hơn độ tinh khiết.

Tôi chắc chắn sẽ nhìn lại câu trả lời này trong một vài năm (tháng / ngày) và không đồng ý với bản thân vì cách tiếp cận của tôi đã thay đổi - tuy nhiên đây là cách tiếp cận hiện tại của tôi.

Để thử và tổng hợp mọi thứ tôi đã nói ở trên, đây là thử nghiệm tích hợp DB điển hình của tôi:

[Test]
public void LoadUser()
{
  this.RunTest(session => // the NH/EF session to attach the objects to
  {
    var user = new UserAccount("Mr", "Joe", "Bloggs");
    session.Save(user);
    return user.UserID;
  }, id => // the ID of the entity we need to load
  {
     var user = LoadMyUser(id); // load the entity
     Assert.AreEqual("Mr", user.Title); // test your properties
     Assert.AreEqual("Joe", user.Firstname);
     Assert.AreEqual("Bloggs", user.Lastname);
  }
}

Điều quan trọng cần lưu ý ở đây là các phiên của hai vòng hoàn toàn độc lập. Khi triển khai RunTest, bạn phải đảm bảo rằng bối cảnh được cam kết và phá hủy và dữ liệu của bạn chỉ có thể đến từ cơ sở dữ liệu của bạn cho phần thứ hai.

Chỉnh sửa ngày 13/10/2014

Tôi đã nói rằng tôi có thể sửa đổi mô hình này trong những tháng tới. Trong khi tôi chủ yếu theo cách tiếp cận mà tôi ủng hộ ở trên, tôi đã cập nhật cơ chế thử nghiệm của mình một chút. Bây giờ tôi có xu hướng tạo các thực thể trong TestSetup và TestTearDown.

[SetUp]
public void Setup()
{
  this.SetupTest(session => // the NH/EF session to attach the objects to
  {
    var user = new UserAccount("Mr", "Joe", "Bloggs");
    session.Save(user);
    this.UserID =  user.UserID;
  });
}

[TearDown]
public void TearDown()
{
   this.TearDownDatabase();
}

Sau đó kiểm tra từng thuộc tính riêng lẻ

[Test]
public void TestTitle()
{
     var user = LoadMyUser(this.UserID); // load the entity
     Assert.AreEqual("Mr", user.Title);
}

[Test]
public void TestFirstname()
{
     var user = LoadMyUser(this.UserID);
     Assert.AreEqual("Joe", user.Firstname);
}

[Test]
public void TestLastname()
{
     var user = LoadMyUser(this.UserID);
     Assert.AreEqual("Bloggs", user.Lastname);
}

Có một số lý do cho cách tiếp cận này:

  • Không có cuộc gọi cơ sở dữ liệu bổ sung (một thiết lập, một lần xé)
  • Các xét nghiệm có độ chi tiết cao hơn, mỗi thử nghiệm xác minh một thuộc tính
  • Logic thiết lập / TearDown bị xóa khỏi chính các phương thức Test

Tôi cảm thấy điều này làm cho lớp kiểm tra đơn giản hơn và các bài kiểm tra chi tiết hơn (các xác nhận đơn là tốt )

Chỉnh sửa 5/3/2015

Một sửa đổi khác về phương pháp này. Mặc dù các thiết lập cấp độ lớp rất hữu ích cho các thử nghiệm, chẳng hạn như tải các thuộc tính, nhưng chúng ít hữu ích hơn khi yêu cầu các thiết lập khác nhau. Trong trường hợp này, thiết lập một lớp mới cho mỗi trường hợp là quá mức cần thiết.

Để giúp với điều này bây giờ tôi có xu hướng có hai lớp cơ sở SetupPerTestSingleSetup. Hai lớp này phơi bày khung theo yêu cầu.

Trong SingleSetupchúng tôi có một cơ chế rất giống như được mô tả trong lần chỉnh sửa đầu tiên của tôi. Một ví dụ sẽ là

public TestProperties : SingleSetup
{
  public int UserID {get;set;}

  public override DoSetup(ISession session)
  {
    var user = new User("Joe", "Bloggs");
    session.Save(user);
    this.UserID = user.UserID;
  }

  [Test]
  public void TestLastname()
  {
     var user = LoadMyUser(this.UserID); // load the entity
     Assert.AreEqual("Bloggs", user.Lastname);
  }

  [Test]
  public void TestFirstname()
  {
       var user = LoadMyUser(this.UserID);
       Assert.AreEqual("Joe", user.Firstname);
  }
}

Tuy nhiên, các tham chiếu đảm bảo rằng chỉ các mục nhập chính xác được tải mới có thể sử dụng phương pháp SetupPerTest

public TestProperties : SetupPerTest
{
   [Test]
   public void EnsureCorrectReferenceIsLoaded()
   {
      int friendID = 0;
      this.RunTest(session =>
      {
         var user = CreateUserWithFriend();
         session.Save(user);
         friendID = user.Friends.Single().FriendID;
      } () =>
      {
         var user = GetUser();
         Assert.AreEqual(friendID, user.Friends.Single().FriendID);
      });
   }
   [Test]
   public void EnsureOnlyCorrectFriendsAreLoaded()
   {
      int userID = 0;
      this.RunTest(session =>
      {
         var user = CreateUserWithFriends(2);
         var user2 = CreateUserWithFriends(5);
         session.Save(user);
         session.Save(user2);
         userID = user.UserID;
      } () =>
      {
         var user = GetUser(userID);
         Assert.AreEqual(2, user.Friends.Count());
      });
   }
}

Tóm lại cả hai phương pháp đều hoạt động tùy thuộc vào những gì bạn đang cố gắng kiểm tra.


2
Đây là một cách tiếp cận khác nhau để thử nghiệm tích hợp. TL; DR - Sử dụng chính ứng dụng để thiết lập dữ liệu thử nghiệm, phục hồi giao dịch cho mỗi thử nghiệm.
Gert Arnold

3
@Liath, phản ứng tuyệt vời. Bạn đã xác nhận những nghi ngờ của tôi về việc thử nghiệm EF. Câu hỏi của tôi là thế này; ví dụ của bạn là cho một trường hợp rất cụ thể, đó là tốt. Tuy nhiên, như bạn lưu ý, bạn có thể cần kiểm tra hàng trăm thực thể. Để tuân thủ nguyên tắc DRY (Đừng lặp lại chính mình), làm thế nào để bạn chia tỷ lệ giải pháp của mình mà không lặp lại cùng một mẫu mã cơ bản mỗi lần?
Jeffrey A. Gochin

4
Tôi phải không đồng ý với điều này vì nó hoàn toàn vượt qua vấn đề. Kiểm thử đơn vị là về kiểm tra logic của hàm. Trong ví dụ OP, logic có sự phụ thuộc vào kho lưu trữ dữ liệu. Bạn đúng khi bạn nói không kiểm tra EF, nhưng đó không phải là vấn đề. Vấn đề là kiểm tra mã của bạn một cách tách biệt khỏi kho dữ liệu. Kiểm tra bản đồ của bạn là một chủ đề hoàn toàn khác imo. Để kiểm tra logic có tương tác với dữ liệu chính xác không, bạn cần có khả năng kiểm soát cửa hàng.
Tạp chí Sina 8/8/2016

7
Không ai ở trên hàng rào về việc bạn có nên tự mình kiểm tra Entity Framework hay không. Điều gì xảy ra là bạn cần kiểm tra một số phương thức thực hiện một số nội dung và cũng xảy ra để thực hiện cuộc gọi EF đến cơ sở dữ liệu. Mục tiêu là giả định EF để bạn có thể kiểm tra phương pháp này mà không yêu cầu cơ sở dữ liệu trên máy chủ xây dựng của mình.
Người đàn ông Muffin

4
Tôi thực sự thích cuộc hành trình. Cảm ơn vì đã thêm các chỉnh sửa theo thời gian - nó giống như đọc kiểm soát nguồn và hiểu cách suy nghĩ của bạn đã phát triển. Tôi thực sự đánh giá cao sự khác biệt về chức năng (với EF) và đơn vị (giả định là EF).
Tom Leys

21

Nỗ lực kinh nghiệm Phản hồi tại đây

Sau khi đọc rất nhiều, tôi đã sử dụng Effort trong các bài kiểm tra của mình: trong các bài kiểm tra, Bối cảnh được xây dựng bởi một nhà máy trả về một phiên bản trong bộ nhớ, cho phép tôi kiểm tra một bản trống mỗi lần. Ngoài các thử nghiệm, nhà máy được phân giải thành một trong đó trả về toàn bộ Bối cảnh.

Tuy nhiên tôi có cảm giác rằng việc kiểm tra đối với một bản mô phỏng đầy đủ tính năng của cơ sở dữ liệu có xu hướng kéo các bài kiểm tra xuống; bạn nhận ra rằng bạn phải quan tâm đến việc thiết lập một loạt các phụ thuộc để kiểm tra một phần của hệ thống. Bạn cũng có xu hướng trôi dạt theo hướng tổ chức các bài kiểm tra có thể không liên quan, chỉ vì chỉ có một đối tượng khổng lồ xử lý mọi thứ. Nếu bạn không chú ý, bạn có thể thấy mình đang thực hiện kiểm tra tích hợp thay vì kiểm tra đơn vị

Tôi muốn thử nghiệm dựa trên thứ gì đó trừu tượng hơn là một DBContext khổng lồ nhưng tôi không thể tìm thấy điểm ngọt ngào giữa các xét nghiệm có ý nghĩa và xét nghiệm xương trần. Phấn nó lên đến kinh nghiệm của tôi.

Vì vậy, tôi thấy Nỗ lực thú vị; nếu bạn cần chạy xuống đất thì đó là một công cụ tốt để nhanh chóng bắt đầu và nhận kết quả. Tuy nhiên tôi nghĩ rằng một cái gì đó thanh lịch và trừu tượng hơn một chút nên là bước tiếp theo và đó là điều tôi sẽ điều tra tiếp theo. Thích bài này để xem nó đi đâu tiếp theo :)

Chỉnh sửa để thêm : Nỗ lực mất một chút thời gian để làm nóng, vì vậy bạn đang xem khoảng. 5 giây khi bắt đầu thử nghiệm. Đây có thể là một vấn đề cho bạn nếu bạn cần bộ thử nghiệm của mình rất hiệu quả.


Chỉnh sửa để làm rõ:

Tôi đã sử dụng Effort để kiểm tra ứng dụng webservice. Mỗi tin nhắn M nhập vào được chuyển đến một IHandlerOf<M>thông qua Windsor. Castle.Windsor giải quyết cái IHandlerOf<M>mà giải quyết các phụ thuộc của thành phần. Một trong những phụ thuộc này là DataContextFactory, cho phép người xử lý yêu cầu nhà máy

Trong các thử nghiệm của mình, tôi khởi tạo trực tiếp thành phần IHandlerOf, giả lập tất cả các thành phần phụ của SUT và xử lý Effort-quấn DataContextFactorycho trình xử lý.

Điều đó có nghĩa là tôi không kiểm tra đơn vị theo nghĩa nghiêm ngặt, vì DB bị các bài kiểm tra của tôi tấn công. Tuy nhiên như tôi đã nói ở trên, nó cho phép tôi chạy xuống đất và tôi có thể nhanh chóng kiểm tra một số điểm trong ứng dụng


Cảm ơn về đầu vào, những gì tôi có thể làm khi tôi thực hiện dự án này vì đây là công việc trả tiền bằng bonafide được bắt đầu với một số repos và xem cách tôi tiếp tục, nhưng Effort rất thú vị. Không quan tâm đến lớp nào bạn đã sử dụng nỗ lực trong các ứng dụng của mình?
Modika

2
chỉ khi Effort đã hỗ trợ giao dịch đúng cách
Sedat Kapanoglu 17/12/14

và nỗ lực có lỗi đối với các chuỗi với trình tải csv, khi chúng ta sử dụng '' thay vì null trong chuỗi.
Sam

13

Nếu bạn muốn đơn vị kiểm tra mã thì bạn cần cách ly mã bạn muốn kiểm tra (trong trường hợp này là dịch vụ của bạn) khỏi các tài nguyên bên ngoài (ví dụ: cơ sở dữ liệu). Bạn có thể có thể làm điều này với một số loại nhà cung cấp EF trong bộ nhớ , tuy nhiên một cách phổ biến hơn nhiều là trừu tượng hóa việc triển khai EF của bạn, ví dụ như với một số mẫu kho lưu trữ. Không có sự cô lập này, bất kỳ bài kiểm tra nào bạn viết sẽ là kiểm tra tích hợp, không phải kiểm tra đơn vị.

Đối với việc kiểm tra mã EF - Tôi viết các kiểm tra tích hợp tự động cho các kho lưu trữ của tôi, ghi các hàng khác nhau vào cơ sở dữ liệu trong quá trình khởi tạo và sau đó gọi các triển khai kho lưu trữ của tôi để đảm bảo rằng chúng hoạt động như mong đợi (ví dụ: đảm bảo rằng các kết quả được lọc chính xác hoặc rằng chúng được sắp xếp theo đúng thứ tự).

Đây là các thử nghiệm tích hợp chứ không phải thử nghiệm đơn vị, vì các thử nghiệm dựa trên việc có kết nối cơ sở dữ liệu và cơ sở dữ liệu đích đã cài đặt lược đồ cập nhật mới nhất.


Cảm ơn @justin tôi biết về mẫu Kho lưu trữ, nhưng việc đọc những thứ như ayende.com/blog/4784/iêuLostechies.com/jimmybogard/2009/09/11/wither-the-reposeective trong số những người khác đã khiến tôi nghĩ rằng tôi không Chúng tôi không muốn lớp trừu tượng này, nhưng một lần nữa chúng lại nói nhiều hơn về cách tiếp cận Truy vấn cũng rất khó hiểu.
Modika

7
@Modika Ayende đã chọn triển khai mô hình kho lưu trữ kém để phê bình, và kết quả là đúng 100% - nó được thiết kế quá mức và không mang lại bất kỳ lợi ích nào. Một triển khai tốt tách biệt các phần có thể kiểm tra đơn vị của mã của bạn khỏi triển khai DAL. Việc sử dụng NHibernate và EF trực tiếp làm cho mã trở nên khó khăn (nếu không nói là không thể) để kiểm tra đơn vị và dẫn đến một cơ sở mã hóa nguyên khối cứng nhắc. Tôi vẫn hơi nghi ngờ về mẫu kho lưu trữ, tuy nhiên tôi tin chắc 100% rằng bạn cần cách ly việc triển khai DAL của mình bằng cách nào đó và kho lưu trữ là thứ tốt nhất tôi tìm thấy cho đến nay.
Justin

2
@Modika Đọc bài viết thứ hai một lần nữa. "Tôi không muốn lớp trừu tượng này" không phải là những gì anh ấy nói. Ngoài ra, hãy đọc về mẫu Kho lưu trữ ban đầu từ Fowler ( martinfowler.com/eaaCatalog/reposeective.html ) hoặc DDD ( dddcommunity.org/resource/ddd_terms ). Đừng tin những người không tán thành mà không hiểu đầy đủ về khái niệm ban đầu. Những gì họ thực sự chỉ trích là một sự lạm dụng mẫu gần đây, chứ không phải bản thân mẫu (mặc dù họ có thể không biết điều này).
guillaume31

1
@ guillaume31 tôi không chống lại mô hình kho lưu trữ và sử dụng nó trong các thử nghiệm của tôi ở một lớp cao hơn trong ứng dụng của tôi. Ngoài ra, nếu tôi không sử dụng repo, tôi sẽ nhận được lợi ích của bộ tính năng mở rộng của EF, với một repo tôi có thể không nhận được điều đó.
Modika

Khi tôi đã tách DAL với một kho lưu trữ, tôi cần một cách nào đó để "Mock" cơ sở dữ liệu (EF). Cho đến nay, việc chế nhạo bối cảnh và các phần mở rộng async khác nhau (ToListAsync (), FirstOrDefaultAsync (), v.v.) đã dẫn đến sự thất vọng cho tôi.
Kevin Burton

9

Vì vậy, đây là vấn đề, Entity Framework là một triển khai vì vậy mặc dù thực tế là nó trừu tượng hóa sự phức tạp của tương tác cơ sở dữ liệu, tương tác trực tiếp vẫn là khớp nối chặt chẽ và đó là lý do khiến nó khó kiểm tra.

Kiểm thử đơn vị là về kiểm tra logic của một chức năng và từng kết quả tiềm năng của nó tách biệt với mọi phụ thuộc bên ngoài, trong trường hợp này là lưu trữ dữ liệu. Để làm được điều đó, bạn cần có khả năng kiểm soát hành vi của kho lưu trữ dữ liệu. Ví dụ: nếu bạn muốn xác nhận rằng hàm của bạn trả về false nếu người dùng đã tìm nạp không đáp ứng một số bộ tiêu chí, thì kho dữ liệu [giả định] của bạn phải được định cấu hình để luôn trả về người dùng không đáp ứng tiêu chí và ngược lại ngược lại cho khẳng định ngược lại.

Như đã nói, và chấp nhận thực tế rằng EF là một triển khai, tôi có thể sẽ ủng hộ ý tưởng trừu tượng hóa một kho lưu trữ. Có vẻ hơi dư thừa? Không, bởi vì bạn đang giải quyết một vấn đề cô lập mã của bạn khỏi việc triển khai dữ liệu.

Trong DDD, các kho lưu trữ chỉ trả về các gốc tổng hợp, không phải DAO. Theo cách đó, người tiêu dùng của kho lưu trữ không bao giờ phải biết về việc triển khai dữ liệu (vì nó không nên) và chúng ta có thể sử dụng nó như một ví dụ về cách giải quyết vấn đề này. Trong trường hợp này, đối tượng được tạo bởi EF là DAO và do đó, nên được ẩn khỏi ứng dụng của bạn. Đây là một lợi ích khác của kho lưu trữ mà bạn xác định. Bạn có thể định nghĩa một đối tượng kinh doanh là loại trả về của nó thay vì đối tượng EF. Bây giờ những gì repo làm là ẩn các lệnh gọi tới EF và ánh xạ phản hồi của EF tới đối tượng kinh doanh được xác định trong chữ ký repos. Bây giờ bạn có thể sử dụng repo đó thay cho phụ thuộc DbContext mà bạn đưa vào các lớp của mình và do đó, bây giờ bạn có thể giả định giao diện đó để cung cấp cho bạn quyền kiểm soát mà bạn cần để kiểm tra mã riêng lẻ.

Đó là một công việc nhiều hơn và nhiều ngón tay cái của họ vào nó, nhưng nó giải quyết một vấn đề thực sự. Có một nhà cung cấp trong bộ nhớ được đề cập trong một câu trả lời khác có thể là một lựa chọn (tôi chưa thử) và chính sự tồn tại của nó là bằng chứng về sự cần thiết phải thực hành.

Tôi hoàn toàn không đồng ý với câu trả lời hàng đầu vì nó vượt qua vấn đề thực sự là cách ly mã của bạn và sau đó tiếp tục kiểm tra bản đồ của bạn. Bằng mọi cách hãy kiểm tra bản đồ của bạn nếu bạn muốn, nhưng giải quyết vấn đề thực tế ở đây và nhận được một số bảo hiểm mã thực sự.


8

Tôi sẽ không đơn vị kiểm tra mã tôi không sở hữu. Bạn đang thử nghiệm gì ở đây, trình biên dịch MSFT hoạt động?

Điều đó nói rằng, để làm cho mã này có thể kiểm tra được, bạn gần như phải làm cho lớp truy cập dữ liệu của bạn tách biệt với mã logic nghiệp vụ của bạn. Những gì tôi làm là lấy tất cả các công cụ EF của tôi và đặt nó vào một lớp (hoặc nhiều) DAO hoặc DAL cũng có giao diện tương ứng. Sau đó, tôi viết dịch vụ của mình sẽ có đối tượng DAO hoặc DAL được thêm vào dưới dạng phụ thuộc (tốt nhất là hàm tạo) được gọi là giao diện. Bây giờ phần cần được kiểm tra (mã của bạn) có thể dễ dàng được kiểm tra bằng cách giả lập giao diện DAO và đưa phần đó vào ví dụ dịch vụ của bạn trong bài kiểm tra đơn vị của bạn.

//this is testable just inject a mock of IProductDAO during unit testing
public class ProductService : IProductService
{
    private IProductDAO _productDAO;

    public ProductService(IProductDAO productDAO)
    {
        _productDAO = productDAO;
    }

    public List<Product> GetAllProducts()
    {
        return _productDAO.GetAll();
    }

    ...
}

Tôi sẽ coi các lớp truy cập dữ liệu trực tiếp là một phần của kiểm thử tích hợp, không phải kiểm thử đơn vị. Tôi đã thấy các chàng trai kiểm tra xem có bao nhiêu chuyến đi đến cơ sở dữ liệu ngủ đông thực hiện trước đó, nhưng họ đã ở trong một dự án liên quan đến hàng tỷ hồ sơ trong kho dữ liệu của họ và những chuyến đi thêm đó thực sự quan trọng.


1
Cảm ơn câu trả lời, nhưng sự khác biệt của điều này sẽ là gì khi nói một Kho lưu trữ nơi bạn đang ẩn nội bộ của EF đằng sau nó ở cấp độ này? Tôi không thực sự muốn trừu tượng hóa EF, mặc dù tôi vẫn có thể làm điều đó với giao diện IContext? Tôi mới biết điều này, hãy nhẹ nhàng :)
Modika

3
@Modika Một Repo cũng tốt. Bất cứ mô hình nào bạn muốn. "Tôi không thực sự muốn trừu tượng EF" Bạn có muốn kiểm tra mã hay không?
Jonathan Henson

1
@Modika quan điểm của tôi là bạn sẽ không có BẤT K code mã có thể kiểm tra nào nếu bạn không tách rời mối quan tâm của mình. Truy cập dữ liệu và logic nghiệp vụ PHẢI ở trong các lớp riêng biệt để thực hiện các bài kiểm tra duy trì tốt.
Jonathan Henson

2
Tôi chỉ không cảm thấy cần thiết phải bọc EF trong một bản tóm tắt kho lưu trữ vì về cơ bản IDbSets là repo và bối cảnh của UOW, tôi sẽ cập nhật câu hỏi của mình một chút vì điều đó có thể gây hiểu nhầm. Vấn đề đi kèm với bất kỳ sự trừu tượng nào và điểm chính là chính xác những gì tôi đang kiểm tra vì hàng đợi của tôi sẽ không chạy trong cùng một ranh giới (linq-to-entity vs linq-to-object) vì vậy nếu tôi chỉ kiểm tra thì dịch vụ của tôi tạo ra gọi đó có vẻ hơi lãng phí hay tôi đang ở đây?
Modika

1
, Mặc dù tôi đồng ý với các điểm chung của bạn, DbContext là một đơn vị công việc và IDbSets chắc chắn là một số để thực hiện kho lưu trữ và tôi không phải là người duy nhất nghĩ rằng. Tôi có thể chế giễu EF và ở một số lớp tôi sẽ cần chạy thử nghiệm tích hợp, điều đó có thực sự quan trọng nếu tôi thực hiện trong Kho lưu trữ hoặc tiếp tục trong Dịch vụ không? Việc kết hợp chặt chẽ với DB không thực sự đáng lo ngại, tôi chắc chắn điều đó xảy ra nhưng tôi sẽ không lên kế hoạch cho điều gì đó có thể không xảy ra.
Modika

8

Tôi đã dò dẫm một lúc nào đó để đạt được những cân nhắc sau:

1- Nếu ứng dụng của tôi truy cập cơ sở dữ liệu, tại sao kiểm tra không nên? Điều gì nếu có vấn đề với việc truy cập dữ liệu? Các bài kiểm tra phải biết trước và cảnh báo bản thân về vấn đề.

2- Mẫu Kho lưu trữ hơi khó và tốn thời gian.

Vì vậy, tôi đã đưa ra cách tiếp cận này, mà tôi không nghĩ là tốt nhất, nhưng đáp ứng mong đợi của tôi:

Use TransactionScope in the tests methods to avoid changes in the database.

Để làm điều đó là cần thiết:

1- Cài đặt EntityFramework vào Dự án thử nghiệm. 2- Đặt chuỗi kết nối vào tệp app.config của Dự án thử nghiệm. 3- Tham khảo dll System.Transilities trong Test Project.

Tác dụng phụ duy nhất là hạt giống nhận dạng sẽ tăng lên khi cố gắng chèn, ngay cả khi giao dịch bị hủy bỏ. Nhưng vì các thử nghiệm được thực hiện đối với cơ sở dữ liệu phát triển, nên điều này không có vấn đề gì.

Mã mẫu:

[TestClass]
public class NameValueTest
{
    [TestMethod]
    public void Edit()
    {
        NameValueController controller = new NameValueController();

        using(var ts = new TransactionScope()) {
            Assert.IsNotNull(controller.Edit(new Models.NameValue()
            {
                NameValueId = 1,
                name1 = "1",
                name2 = "2",
                name3 = "3",
                name4 = "4"
            }));

            //no complete, automatically abort
            //ts.Complete();
        }
    }

    [TestMethod]
    public void Create()
    {
        NameValueController controller = new NameValueController();

        using (var ts = new TransactionScope())
        {
            Assert.IsNotNull(controller.Create(new Models.NameValue()
            {
                name1 = "1",
                name2 = "2",
                name3 = "3",
                name4 = "4"
            }));

            //no complete, automatically abort
            //ts.Complete();
        }
    }
}

1
Thật ra, tôi thích giải pháp này rất nhiều. Siêu đơn giản để thực hiện và các kịch bản thử nghiệm thực tế hơn. Cảm ơn!
slopapa

1
với EF 6, bạn sẽ sử dụng DbContext.Database.BeginTransaction, phải không?
SwissCoder

5

Nói tóm lại, tôi sẽ nói không, nước trái cây không đáng để ép để thử nghiệm một phương pháp dịch vụ với một dòng duy nhất lấy dữ liệu mô hình. Theo kinh nghiệm của tôi, những người mới làm quen với TDD muốn kiểm tra hoàn toàn mọi thứ. Hạt dẻ cũ của việc trừu tượng hóa mặt tiền sang khung của bên thứ 3 chỉ để bạn có thể tạo một mô hình API của khung đó mà bạn bastardise / mở rộng để bạn có thể tiêm dữ liệu giả có giá trị rất ít trong tâm trí tôi. Mọi người có một cái nhìn khác nhau về việc kiểm tra đơn vị bao nhiêu là tốt nhất. Tôi có xu hướng thực dụng hơn những ngày này và tự hỏi liệu thử nghiệm của tôi có thực sự tăng thêm giá trị cho sản phẩm cuối cùng không, và với giá nào.


1
Có để thực dụng. Tôi vẫn cho rằng chất lượng kiểm tra đơn vị của bạn kém hơn chất lượng của mã gốc. Tất nhiên có giá trị trong việc sử dụng TDD để cải thiện thực hành mã hóa của bạn và cũng để tăng cường khả năng bảo trì, nhưng TDD có thể có giá trị giảm dần. Chúng tôi chạy tất cả các thử nghiệm của chúng tôi dựa trên cơ sở dữ liệu, bởi vì nó mang lại cho chúng tôi sự tự tin rằng việc sử dụng EF và của chính các bảng là âm thanh. Các bài kiểm tra mất nhiều thời gian hơn để chạy, nhưng chúng đáng tin cậy hơn.
Savage

3

Tôi muốn chia sẻ một cách tiếp cận được bình luận và thảo luận ngắn gọn nhưng chỉ ra một ví dụ thực tế mà tôi hiện đang sử dụng để giúp đơn vị thử nghiệm các dịch vụ dựa trên EF.

Đầu tiên, tôi rất thích sử dụng nhà cung cấp bộ nhớ trong từ EF Core, nhưng đây là về EF 6. Ngoài ra, đối với các hệ thống lưu trữ khác như RavenDB, tôi cũng sẽ là người đề xuất thử nghiệm thông qua nhà cung cấp cơ sở dữ liệu trong bộ nhớ. Một lần nữa - điều này đặc biệt để giúp kiểm tra mã dựa trên EF mà không cần nhiều nghi lễ .

Dưới đây là các mục tiêu tôi đã có khi đưa ra một mô hình:

  • Nó phải đơn giản để các nhà phát triển khác trong nhóm hiểu
  • Nó phải cách ly mã EF ở mức thấp nhất có thể
  • Nó không được liên quan đến việc tạo các giao diện đa trách nhiệm kỳ lạ (như mẫu kho lưu trữ "chung" hoặc "điển hình")
  • Nó phải dễ dàng để cấu hình và thiết lập trong một bài kiểm tra đơn vị

Tôi đồng ý với các tuyên bố trước đây rằng EF vẫn là một chi tiết triển khai và cảm thấy như bạn cần phải trừu tượng hóa nó để thực hiện một bài kiểm tra đơn vị "thuần túy". Tôi cũng đồng ý rằng lý tưởng là tôi muốn đảm bảo mã EF hoạt động - nhưng điều này liên quan đến cơ sở dữ liệu hộp cát, nhà cung cấp trong bộ nhớ, v.v ... Cách tiếp cận của tôi giải quyết cả hai vấn đề - bạn có thể kiểm tra đơn giản mã phụ thuộc vào EF tạo kiểm tra tích hợp để kiểm tra mã EF của bạn một cách cụ thể.

Cách tôi đạt được điều này là thông qua việc gói mã EF vào các lớp Truy vấn và Lệnh chuyên dụng. Ý tưởng rất đơn giản: chỉ cần bọc bất kỳ mã EF nào trong một lớp và phụ thuộc vào giao diện trong các lớp ban đầu đã sử dụng nó. Vấn đề chính tôi cần giải quyết là tránh thêm nhiều phụ thuộc vào các lớp và thiết lập nhiều mã trong các thử nghiệm của mình.

Đây là nơi có một thư viện đơn giản, hữu ích xuất hiện: Mediatr . Nó cho phép nhắn tin trong quá trình đơn giản và thực hiện bằng cách tách "yêu cầu" từ các trình xử lý thực thi mã. Điều này có thêm một lợi ích của việc tách rời "cái gì" từ "làm thế nào". Ví dụ: bằng cách gói mã EF thành các phần nhỏ, nó cho phép bạn thay thế các cài đặt bằng một nhà cung cấp khác hoặc cơ chế hoàn toàn khác, bởi vì tất cả những gì bạn đang làm là gửi yêu cầu thực hiện một hành động.

Bằng cách sử dụng phép nội xạ phụ thuộc (có hoặc không có khung - tùy chọn của bạn), chúng tôi có thể dễ dàng chế giễu người hòa giải và kiểm soát các cơ chế yêu cầu / phản hồi để cho phép kiểm tra đơn vị mã EF.

Đầu tiên, giả sử chúng ta có một dịch vụ có logic nghiệp vụ mà chúng ta cần kiểm tra:

public class FeatureService {

  private readonly IMediator _mediator;

  public FeatureService(IMediator mediator) {
    _mediator = mediator;
  }

  public async Task ComplexBusinessLogic() {
    // retrieve relevant objects

    var results = await _mediator.Send(new GetRelevantDbObjectsQuery());
    // normally, this would have looked like...
    // var results = _myDbContext.DbObjects.Where(x => foo).ToList();

    // perform business logic
    // ...    
  }
}

Bạn có bắt đầu thấy lợi ích của phương pháp này? Bạn không chỉ đóng gói rõ ràng tất cả các mã liên quan đến EF vào các lớp mô tả, bạn còn cho phép mở rộng bằng cách loại bỏ mối quan tâm thực hiện "cách" yêu cầu này - lớp này không quan tâm nếu các đối tượng có liên quan đến từ EF, MongoDB, hoặc một tập tin văn bản.

Bây giờ cho yêu cầu và xử lý, thông qua MediatR:

public class GetRelevantDbObjectsQuery : IRequest<DbObject[]> {
  // no input needed for this particular request,
  // but you would simply add plain properties here if needed
}

public class GetRelevantDbObjectsEFQueryHandler : IRequestHandler<GetRelevantDbObjectsQuery, DbObject[]> {
  private readonly IDbContext _db;

  public GetRelevantDbObjectsEFQueryHandler(IDbContext db) {
    _db = db;
  }

  public DbObject[] Handle(GetRelevantDbObjectsQuery message) {
    return _db.DbObjects.Where(foo => bar).ToList();
  }
}

Như bạn có thể thấy, sự trừu tượng là đơn giản và được gói gọn. Điều này cũng hoàn toàn có thể kiểm tra được vì trong một bài kiểm tra tích hợp, bạn có thể kiểm tra riêng lớp này - không có mối quan tâm kinh doanh nào ở đây.

Vì vậy, một bài kiểm tra đơn vị của dịch vụ tính năng của chúng tôi trông như thế nào? Thật đơn giản. Trong trường hợp này, tôi đang sử dụng Moq để chế giễu (sử dụng bất cứ điều gì khiến bạn hài lòng):

[TestClass]
public class FeatureServiceTests {

  // mock of Mediator to handle request/responses
  private Mock<IMediator> _mediator;

  // subject under test
  private FeatureService _sut;

  [TestInitialize]
  public void Setup() {

    // set up Mediator mock
    _mediator = new Mock<IMediator>(MockBehavior.Strict);

    // inject mock as dependency
    _sut = new FeatureService(_mediator.Object);
  }

  [TestCleanup]
  public void Teardown() {

    // ensure we have called or expected all calls to Mediator
    _mediator.VerifyAll();
  }

  [TestMethod]
  public void ComplexBusinessLogic_Does_What_I_Expect() {
    var dbObjects = new List<DbObject>() {
      // set up any test objects
      new DbObject() { }
    };

    // arrange

    // setup Mediator to return our fake objects when it receives a message to perform our query
    // in practice, I find it better to create an extension method that encapsulates this setup here
    _mediator.Setup(x => x.Send(It.IsAny<GetRelevantDbObjectsQuery>(), default(CancellationToken)).ReturnsAsync(dbObjects.ToArray()).Callback(
    (GetRelevantDbObjectsQuery message, CancellationToken token) => {
       // using Moq Callback functionality, you can make assertions
       // on expected request being passed in
       Assert.IsNotNull(message);
    });

    // act
    _sut.ComplexBusinessLogic();

    // assertions
  }

}

Bạn có thể thấy tất cả những gì chúng tôi cần là một thiết lập duy nhất và chúng tôi thậm chí không cần định cấu hình thêm bất cứ điều gì - đó là một thử nghiệm đơn vị rất đơn giản. Chúng ta hãy rõ ràng: Điều này hoàn toàn có thể làm mà không cần một cái gì đó như Mediatr (bạn chỉ cần thực hiện một giao diện và giả định nó để kiểm tra, ví dụ IGetRelevantDbObjectsQuery), nhưng trong thực tế cho một cơ sở mã lớn với nhiều tính năng và truy vấn / lệnh, tôi yêu việc đóng gói và hỗ trợ DI bẩm sinh Mediatr cung cấp.

Nếu bạn đang tự hỏi làm thế nào tôi tổ chức các lớp này, thì nó khá đơn giản:

- MyProject
  - Features
    - MyFeature
      - Queries
      - Commands
      - Services
      - DependencyConfig.cs (Ninject feature modules)

Sắp xếp theo các lát tính năng nằm bên cạnh điểm, nhưng điều này giữ cho tất cả các mã có liên quan / phụ thuộc cùng nhau và có thể dễ dàng khám phá. Quan trọng nhất, tôi tách các Truy vấn và Lệnh - theo nguyên tắc Phân tách Lệnh / Truy vấn .

Điều này đáp ứng tất cả các tiêu chí của tôi: đó là lễ thấp, dễ hiểu và có thêm lợi ích tiềm ẩn. Ví dụ, làm thế nào để bạn xử lý lưu thay đổi? Bây giờ bạn có thể đơn giản hóa Bối cảnh Db của mình bằng cách sử dụng giao diện vai trò (IUnitOfWork.SaveChangesAsync()) và giả lập các cuộc gọi đến giao diện vai trò duy nhất hoặc bạn có thể gói gọn cam kết / quay lại bên trong RequestHandlers của mình - tuy nhiên bạn thích làm điều đó tùy thuộc vào bạn, miễn là có thể duy trì được. Ví dụ, tôi đã cố gắng tạo một yêu cầu / xử lý chung chung trong đó bạn chỉ cần truyền một đối tượng EF và nó sẽ lưu / cập nhật / xóa nó - nhưng bạn phải hỏi ý định của bạn là gì và hãy nhớ rằng nếu bạn muốn trao đổi trình xử lý với nhà cung cấp / triển khai lưu trữ khác, có lẽ bạn nên tạo các lệnh / truy vấn rõ ràng đại diện cho những gì bạn định làm. Thường xuyên hơn không, một dịch vụ hoặc tính năng sẽ cần một cái gì đó cụ thể - không tạo ra những thứ chung chung trước khi bạn có nhu cầu.

Tất nhiên có những cảnh báo cho mô hình này - bạn có thể đi quá xa với một cơ chế pub / sub đơn giản. Tôi đã giới hạn việc triển khai của mình chỉ để trừu tượng hóa mã liên quan đến EF, nhưng các nhà phát triển mạo hiểm có thể bắt đầu sử dụng MediatR để vượt qua mọi thứ và thông báo tất cả mọi thứ - nên thực hiện đánh giá mã tốt và đánh giá ngang hàng. Đó là một vấn đề về quy trình, không phải là vấn đề với MediatR, vì vậy hãy nhận thức rõ về cách bạn sử dụng mẫu này.

Bạn muốn có một ví dụ cụ thể về cách mọi người thử nghiệm đơn vị / chế nhạo EF và đây là một cách tiếp cận thành công với chúng tôi trong dự án của chúng tôi - và nhóm rất hài lòng với việc áp dụng nó dễ dàng như thế nào. Tôi hi vọng cái này giúp được! Như với tất cả mọi thứ trong lập trình, có nhiều cách tiếp cận và tất cả phụ thuộc vào những gì bạn muốn đạt được. Tôi đánh giá cao sự đơn giản, dễ sử dụng, khả năng bảo trì và khả năng khám phá - và giải pháp này đáp ứng tất cả các nhu cầu đó.


Cảm ơn câu trả lời, đây là một mô tả tuyệt vời về Mẫu QueryObject bằng Trình hòa giải và một cái gì đó tôi cũng đang bắt đầu thúc đẩy trong các dự án của mình. Tôi có thể phải cập nhật câu hỏi nhưng tôi không còn kiểm tra đơn vị EF nữa, các tóm tắt quá rò rỉ (SqlLite có thể ổn) mặc dù vậy tôi chỉ kiểm tra những thứ mà tôi truy vấn cơ sở dữ liệu và kiểm tra các quy tắc nghiệp vụ và logic khác.
Modika

3

Có Effort là nhà cung cấp cơ sở dữ liệu khung thực thể trong bộ nhớ. Tôi đã không thực sự thử nó ... Haa chỉ phát hiện ra điều này đã được đề cập trong câu hỏi!

Ngoài ra, bạn có thể chuyển sang EntityFrameworkCore có nhà cung cấp cơ sở dữ liệu trong bộ nhớ tích hợp.

https://blog.goyello.com/2016/07/14/save-time-mocking-use-your-real-entity-framework-dbcontext-in-unit-tests/

https://github.com/tamasflamich/effort

Tôi đã sử dụng một nhà máy để có được một bối cảnh, vì vậy tôi có thể tạo ra bối cảnh gần với việc sử dụng nó. Điều này dường như hoạt động tại địa phương trong studio trực quan nhưng không phải trên máy chủ xây dựng TeamCity của tôi, không biết tại sao.

return new MyContext(@"Server=(localdb)\mssqllocaldb;Database=EFProviders.InMemory;Trusted_Connection=True;");

Xin chào andrew, vấn đề không bao giờ có được bối cảnh, bạn có thể tạo ra bối cảnh đó là những gì chúng tôi đang làm, trừu tượng hóa bối cảnh và được xây dựng bởi nhà máy. Vấn đề lớn nhất là tính nhất quán của những gì trong bộ nhớ so với những gì Linq4Entities làm, chúng không giống nhau có thể dẫn đến các bài kiểm tra sai lệch. Hiện tại, chúng tôi chỉ tích hợp công cụ kiểm tra cơ sở dữ liệu, có thể không phải là quy trình tốt nhất cho mọi người quan tâm đến bạn.
Modika

Trình trợ giúp Moq này hoạt động ( codeproject.com/Tips/1045590/ mẹo ) nếu bạn có một bối cảnh để giả định. Nếu bạn ủng hộ bối cảnh bị nhạo báng với một danh sách thì nó sẽ không hoạt động giống như bối cảnh được hỗ trợ bởi cơ sở dữ liệu sql.
pate

2

Tôi muốn tách các bộ lọc của mình khỏi các phần khác của mã và kiểm tra các bộ lọc khi tôi phác thảo trên blog của mình tại đây http://coding.grax.com/2013/08/testing-custom-linq-filter-operators.html

Điều đó đang được nói, logic bộ lọc đang được kiểm tra không giống với logic bộ lọc được thực thi khi chương trình được chạy do dịch giữa biểu thức LINQ và ngôn ngữ truy vấn cơ bản, chẳng hạn như T-SQL. Tuy nhiên, điều này cho phép tôi xác thực logic của bộ lọc. Tôi không lo lắng quá nhiều về các bản dịch xảy ra và những thứ như phân biệt chữ hoa chữ thường và xử lý null cho đến khi tôi kiểm tra sự tích hợp giữa các lớp.


0

Điều quan trọng là kiểm tra những gì bạn đang mong đợi khung thực thể sẽ làm (nghĩa là xác thực các kỳ vọng của bạn). Một cách để làm điều này mà tôi đã sử dụng thành công, đó là sử dụng moq như trong ví dụ này (để dài để sao chép vào câu trả lời này):

https://docs.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking

Tuy nhiên, hãy cẩn thận ... Một bối cảnh SQL không được đảm bảo để trả lại mọi thứ theo một thứ tự cụ thể trừ khi bạn có một "OrderBy" thích hợp trong truy vấn linq của mình, do đó có thể viết những thứ vượt qua khi bạn kiểm tra bằng danh sách trong bộ nhớ ( linq-to-entity) nhưng thất bại trong môi trường uat / live của bạn khi (linq-to-sql) được sử dụ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.