2010-04-20 20 views
6

Tôi đã cố gắng để bắt đầu với thử nghiệm đơn vị trong khi làm việc trên một chương trình cli nhỏ.Làm thế nào tôi nên viết lại cơ sở dữ liệu của tôi thực hiện/cam kết để làm cho nó tuân theo thử nghiệm đơn vị?

Chương trình của tôi về cơ bản phân tích các đối số dòng lệnh và các tùy chọn và quyết định chức năng nào cần gọi. Mỗi chức năng thực hiện một số hoạt động trên cơ sở dữ liệu.

Vì vậy, ví dụ, tôi có thể có một hàm tạo:

def create(self, opts, args): 
    #I've left out the error handling. 
    strtime = datetime.datetime.now().strftime("%D %H:%M") 
    vals = (strtime, opts.message, opts.keywords, False) 
    self.execute("insert into mytable values (?, ?, ?, ?)", vals) 
    self.commit() 

nên thử nghiệm của tôi trường hợp cuộc gọi chức năng này, sau đó thực hiện các sql chọn để kiểm tra xem hàng đã được nhập? Điều đó nghe có vẻ hợp lý, nhưng cũng làm cho các bài kiểm tra khó khăn hơn để duy trì. Bạn sẽ viết lại hàm để trả về một cái gì đó và kiểm tra giá trị trả về?

Cảm ơn

+0

Tôi nghĩ thử nghiệm đơn vị là hai từ. –

Trả lời

8

Câu trả lời của Alex đề cập đến phương pháp tiêm phụ thuộc. Một yếu tố khác là phương pháp của bạn. Khi nó đứng, nó có hai pha: xây dựng một câu lệnh SQL và thực hiện câu lệnh SQL. Bạn không muốn thử nghiệm giai đoạn thứ hai: bạn không viết động cơ SQL hoặc cơ sở dữ liệu, bạn có thể giả sử chúng hoạt động đúng. Giai đoạn 1 là công việc của bạn: xây dựng một câu lệnh SQL. Vì vậy, bạn có thể tổ chức lại mã để bạn có thể kiểm tra chỉ giai đoạn 1:

def create_sql(self, opts, args): 
    #I've left out the error handling. 
    strtime = datetime.datetime.now().strftime("%D %H:%M") 
    vals = (strtime, opts.message, opts.keywords, False) 
    return "insert into mytable values (?, ?, ?, ?)", vals 

def create(self, opts, args): 
    self.execute(*self.create_sql(opts, args)) 
    self.commit() 

Chức năng create_sql là giai đoạn 1, và bây giờ nó đã bày tỏ trong một cách mà cho phép bạn viết các bài kiểm tra trực tiếp chống lại nó: nó mất giá trị và trả về các giá trị, vì vậy bạn có thể viết các bài kiểm tra đơn vị bao gồm chức năng của nó. Chức năng create bây giờ đơn giản hơn và không cần kiểm tra quá mức: bạn có thể có một vài thử nghiệm cho thấy rằng nó thực sự thực thi SQL đúng cách, nhưng bạn không phải bao gồm tất cả các trường hợp cạnh trong create.

BTW: This video from Pycon (Tests and Testability) có thể thú vị.

6

Tôi chắc chắn sẽ cấu trúc lại phương pháp này để dễ kiểm tra - ví dụ, dependency injection có thể giúp:

def create(self, opts, args, strtime=None, exec_and_commit=None): 
    #I've left out the error handling. 
    if strtime is None: 
     strtime = datetime.datetime.now().strftime("%D %H:%M") 
    vals = (strtime, opts.message, opts.keywords, False) 
    if exec_and_commit is None: 
     exec_and_commit = self.execute_and_commit 
    exec_and_commit("insert into mytable values (?, ?, ?, ?)", vals) 

này dĩ nhiên giả sử bạn có một phương pháp mà các cuộc gọi execute_and_commitexecute và sau đó commit phương pháp. Bằng cách này, mã thử nghiệm có thể tiêm một giá trị đã biết cho strtime và tiêm riêng của nó là exec_and_commit để xác minh rằng nó được gọi với các đối số dự kiến.

0

Không quen thuộc với cú pháp python nhưng nếu bạn mới dùng thử đơn vị, cách dễ nhất để bắt đầu có thể là trích xuất một phương thức mà bạn chuyển vào dòng lệnh args và lấy lại lệnh sql. Trên phương pháp này, bạn có thể kiểm tra phần mã của bạn, nơi logic thực sự nằm. Vượt qua các loại arg khác nhau và kiểm tra kết quả dựa vào lệnh sql sẽ là gì. Một khi bạn bắt đầu để có được hương vị của thử nghiệm đơn vị, bạn có thể tìm hiểu một chút về mocking và phụ thuộc tiêm để kiểm tra nếu bạn gọi một cách chính xác các chức năng cập nhật db.

Hy vọng rằng bạn đã quen thuộc hơn với java C# cú pháp hơn tôi với cú pháp python :)

public string GetSqlCommand(string[] commandLineArgs) 
{ 
    //do your parsing here 
    return sqlCommand; 
} 
[Test] 
public void emptyArgs_returnsEmptySqlCommand() 
{ 
    string expectedSqlCommand=""; 
    assert.AreEqual(expectedSqlCommand, GetSqlCommand(new string[]) 
} 
3

Nói chung, bạn muốn có các bài kiểm tra đơn vị tại chỗ trước khi tái cấu trúc, để đảm bảo không thay đổi vi phạm được thực hiện. Và một số tái cấu trúc có thể cần thiết để cho phép testability ...một nghịch lý không may, một trong đó có chút tôi trước.

Điều đó nói rằng, có một số phép tái cấu trúc nhỏ có thể được thực hiện một cách an toàn mà không thay đổi hành vi. Đổi tên và trích xuất là hai.

Tôi khuyên bạn nên xem cuốn sách của Michael Feathers, Làm việc với Mã kế thừa. Nó tập trung vào mã refactoring cho testability. Các ví dụ là trong Java, nhưng các khái niệm sẽ áp dụng cũng như Python.

+0

và mã C++ được sử dụng trong sách – Gutzofter

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