2009-08-25 53 views
11

Tôi đang sử dụng SqlAlchemy, một thư viện ORM trăn. Và tôi sử dụng để truy cập cơ sở dữ liệu trực tiếp từ lớp kinh doanh trực tiếp bằng cách gọi API SqlAlchemy. Nhưng sau đó tôi thấy rằng sẽ gây ra quá nhiều thời gian để chạy tất cả các trường hợp thử nghiệm của tôi và bây giờ tôi nghĩ rằng có lẽ tôi nên tạo một lớp truy cập DB, vì vậy tôi có thể sử dụng các đối tượng giả trong quá trình kiểm tra thay vì truy cập cơ sở dữ liệu trực tiếp. NGUYÊN NHÂNCách tổ chức lớp Truy cập cơ sở dữ liệu?

Tôi nghĩ có 2 lựa chọn để làm điều đó:

  1. sử dụng một lớp duy nhất, trong đó có một kết nối DB và nhiều phương pháp như adduser/delUser/updateUser, addBook/delBook/updateBook. Nhưng điều này có nghĩa là lớp học này sẽ rất lớn.

  2. Một cách tiếp cận khác là tạo các lớp người quản lý khác nhau như "UserManager", "BookManager". Nhưng điều đó có nghĩa là tôi phải chuyển một danh sách các nhà quản lý đến lớp Business, có vẻ hơi cồng kềnh.

Bạn sẽ tổ chức lớp cơ sở dữ liệu như thế nào?

Trả lời

5

Đó là một câu hỏi hay!
Vấn đề không phải là nhỏ, và có thể yêu cầu một số cách tiếp cận để giải quyết vấn đề. Ví dụ:

  1. Sắp xếp mã để bạn có thể kiểm tra hầu hết logic ứng dụng mà không cần truy cập cơ sở dữ liệu. Điều này có nghĩa là mỗi lớp sẽ có các phương thức để truy cập dữ liệu và các phương thức xử lý dữ liệu, và các phương thức thứ hai có thể được kiểm tra dễ dàng.
  2. Khi bạn cần kiểm tra quyền truy cập cơ sở dữ liệu, bạn có thể sử dụng proxy (vì vậy, như giải pháp # 1); bạn có thể nghĩ về nó như một công cụ cho SqlAlchemy hoặc như là một thay thế thả cho SA. Trong cả hai trường hợp, bạn có thể muốn nghĩ đến một số self initializing fake.
  3. Nếu mã không liên quan đến thủ tục lưu trữ, hãy nghĩ về việc sử dụng cơ sở dữ liệu trong bộ nhớ, như Lennart nói (ngay cả trong trường hợp này, gọi nó là "kiểm thử đơn vị" nghe có vẻ hơi lạ!).

Tuy nhiên, từ kinh nghiệm của tôi, mọi thứ khá dễ dàng, và sau đó rơi đột ngột khi bạn đi thực địa. Ví dụ, phải làm gì khi phần lớn logic nằm trong các câu lệnh SQL? Điều gì sẽ xảy ra nếu truy cập dữ liệu được xen kẽ chặt chẽ với quá trình xử lý của nó? Đôi khi bạn có thể tái cấu trúc, đôi khi (đặc biệt là với các ứng dụng lớn và cũ) không.

Cuối cùng, tôi nghĩ rằng đó chủ yếu là vấn đề của suy nghĩ.
Nếu bạn nghĩ rằng bạn cần phải có bài kiểm tra đơn vị, và bạn cần phải có chúng chạy nhanh, sau đó bạn thiết kế ứng dụng của bạn theo một cách nào đó, cho phép kiểm tra đơn vị dễ dàng hơn. Thật không may, điều này không phải lúc nào cũng đúng (nhiều người thấy các bài kiểm tra đơn vị như một cái gì đó có thể chạy qua đêm, vì vậy thời gian không phải là vấn đề), và bạn sẽ có được một thứ không thể kiểm chứng được.

2

Tôi sẽ thiết lập kết nối cơ sở dữ liệu trong khi thử nghiệm kết nối với cơ sở dữ liệu bộ nhớ thay thế. Cũng giống như vậy:

sqlite_memory_db = create_engine('sqlite://') 

Đó sẽ là khá nhiều nhanh như bạn có thể nhận được, bạn cũng không được kết nối với một cơ sở dữ liệu có thật, nhưng chỉ là tạm thời trong bộ nhớ, do đó bạn không phải lo lắng về các thay đổi được thực hiện bởi các bài kiểm tra của bạn còn lại sau khi kiểm tra, v.v. Và bạn không phải giả lập bất cứ điều gì.

+0

Xin chào, vì một số đồng nghiệp của tôi đã nhấn mạnh rằng chúng tôi nên sử dụng quy trình lưu trữ và tôi không kiểm soát được điều đó, vì vậy sqlite không phải là lựa chọn khả dĩ cho tôi. – ablmf

+0

BTW: sqlite không hỗ trợ quy trình lưu trữ. – ablmf

+0

Hm. Điều này có nghĩa là bạn sẽ phải giả lập các phần quan trọng của mã (các thủ tục được lưu trữ). Điều đó sẽ làm cho các bài kiểm tra ít hữu ích hơn nhiều. Tình huống phức tạp. –

0

SQLAlchemy có một số tiện ích cho making mocking easier - có thể điều đó sẽ dễ dàng hơn việc cố gắng viết lại toàn bộ các phần của dự án của bạn?

+0

Cảm ơn @brool, nhưng liên kết đó đã bị hỏng ngay bây giờ. :( –

2

Một cách để nắm bắt những thay đổi cơ sở dữ liệu, là sử dụng cơ chế mở rộng phiên SQLAlchemy và bừng đánh chặn cơ sở dữ liệu sử dụng một cái gì đó như thế này:

from sqlalchemy.orm.attributes import instance_state 
from sqlalchemy.orm import SessionExtension 

class MockExtension(SessionExtension): 
    def __init__(self): 
     self.clear() 

    def clear(self): 
     self.updates = set() 
     self.inserts = set() 
     self.deletes = set() 

    def before_flush(self, session, flush_context, instances): 
     for obj in session.dirty: 
      self.updates.add(obj) 
      state = instance_state(obj) 
      state.commit_all({}) 
      session.identity_map._mutable_attrs.discard(state) 
      session.identity_map._modified.discard(state) 

     for obj in session.deleted: 
      self.deletes.add(obj) 
      session.expunge(obj) 

     self.inserts.update(session.new) 
     session._new = {} 

Sau đó cho các bài kiểm tra, bạn có thể cấu hình phiên của bạn với mô hình đó và xem liệu nó có phù hợp với mong đợi của bạn hay không.

mock = MockExtension() 
Session = sessionmaker(extension=[mock], expire_on_commit=False) 

def do_something(attr): 
    session = Session() 
    obj = session.query(Cls).first() 
    obj.attr = attr 
    session.commit() 

def test_something(): 
    mock.clear() 
    do_something('foobar') 
    assert len(mock.updates) == 1 
    updated_obj = mock.updates.pop() 
    assert updated_obj.attr == 'foobar' 

Tuy nhiên, bạn sẽ muốn thực hiện ít nhất một số kiểm tra với cơ sở dữ liệu vì ít nhất bạn muốn biết liệu truy vấn của mình có hoạt động như mong đợi không. Và hãy nhớ rằng bạn cũng có thể sửa đổi cơ sở dữ liệu qua session.update(), .delete().execute().

Các vấn đề liên quan