2017-07-10 16 views
5

Tôi có một tệp được lưu ở định dạng cụ thể và một lớp sẽ tạo đối tượng dựa trên dữ liệu trong tệp.Làm thế nào để sử dụng pytest để đảm bảo một đối tượng được tạo ra đúng cách?

Tôi muốn đảm bảo rằng tất cả các giá trị trong tệp/chuỗi được trích xuất một cách chính xác bằng cách kiểm tra từng thuộc tính trong đối tượng.

Đây là một phiên bản đơn giản của những gì tôi đang làm:

classlist.py

import re 

class ClassList: 
    def __init__(self, data): 
     values = re.findall('name=(.*?)\$age=(.*?)\$', data) 

     self.students = [Student(name, int(age)) for name, age in values] 

class Student: 
    def __init__(self, name, age): 
     self.name = name 
     self.age = age 

test_classlist.py

import pytest 
from classlist import ClassList 

def single_data(): 
    text = 'name=alex$age=20$' 
    return ClassList(text) 

def double_data(): 
    text = 'name=taylor$age=23$' \ 
      'name=morgan$age=25$' 
    return ClassList(text) 


@pytest.mark.parametrize('classinfo, expected', [ 
     (single_data(), ['alex']), 
     (double_data(), ['taylor', 'morgan']) 
]) 
def test_name(classinfo, expected): 
    result = [student.name for student in classinfo.students] 

    assert result == expected 

@pytest.mark.parametrize('classinfo, expected', [ 
     (single_data(), [20]), 
     (double_data(), [23, 25]) 
]) 
def test_age(classinfo, expected): 
    result = [student.age for student in classinfo.students] 

    assert result == expected 

Tôi muốn tạo ra các đối tượng dựa trên các dữ liệu khác nhau và sử dụng chúng như một giá trị parametrized.

Thiết lập hiện tại của tôi hoạt động, mặc dù có sự nghe lén không cần thiết về việc tạo đối tượng cho mỗi thử nghiệm. Tôi muốn chúng được tạo ra một lần.

Nếu tôi cố gắng làm như sau:

... 
@pytest.fixture(scope='module') # fixture added 
def double_data(): 
    text = 'name=taylor$age=23$' \ 
      'name=morgan$age=25$' 
    return ClassList(text) 


@pytest.mark.parametrize('classinfo, expected', [ 
     (single_data, ['alex']), 
     (double_data, ['taylor', 'morgan']) #() removed 
]) 
def test_name(classinfo, expected): 
    result = [student.name for student in classinfo.students] 

    assert result == expected 
... 

AttributeError: 'function' object has no attribute 'students'

... nó không hoạt động như nó tham chiếu chức năng chứ không phải là cố định.

Ngoài ra, mã trong test_nametest_age gần như giống hệt nhau. Trong mã thực tế của tôi, tôi đang làm điều này cho khoảng 12 thuộc tính. Nên/điều này có thể được sáp nhập vào một chức năng duy nhất? Làm sao?

Tôi làm cách nào để có thể xóa mã thử nghiệm của mình?

Cảm ơn!

Edit:

Tôi cảm thấy đây là có liên quan, nhưng tôi chắc chắn về cách làm cho nó làm việc cho tình hình của tôi: Can params passed to pytest fixture be passed in as a variable?

Trả lời

2

công trình thiết lập hiện tại của tôi, mặc dù có là không cần thiết nghe lỏm của việc tạo ra các đối tượng cho mỗi bài kiểm tra. Tôi muốn chúng được tạo ra một lần.

này có mùi như không cần thiết trước khi tối ưu hóa đối với tôi, nhưng nếu bạn quan tâm về vấn đề này, sau đó chạy các chức năng mà tạo ra dữ liệu của bạn để kiểm tra ở cấp mô-đun, vì vậy họ chỉ chạy một lần.

Ví dụ:

... 
def single_data(): 
    text = 'name=alex$age=20$' 
    return ClassList(text) 

def double_data(): 
    text = 'name=taylor$age=23$' \ 
      'name=morgan$age=25$' 
    return ClassList(text) 


double_data_object = double_data() 

single_data_object = single_data() 


@pytest.mark.parametrize('classinfo, expected', [ 
     (single_data_object, ['alex']), 
     (double_data_object, ['taylor', 'morgan']) 
]) 
def test_name(classinfo, expected): 
    result = [student.name for student in classinfo.students] 

    assert result == expected 

@pytest.mark.parametrize('classinfo, expected', [ 
     (single_data_object, [20]), 
     (double_data_object, [23, 25]) 
]) 
def test_age(classinfo, expected): 
... 

Bên cạnh đó, các mã trong test_name và test_age là gần như giống hệt nhau. Trong mã thực tế của tôi, tôi đang làm điều này cho khoảng 12 thuộc tính. Nên/có thể điều này được hợp nhất thành một chức năng duy nhất? Làm sao?

Tôi làm cách nào để có thể xóa mã thử nghiệm của mình?

Một vài cách để làm điều này, nhưng từ ví dụ của bạn, cung cấp một equality magic method đến lớp Student và sử dụng để kiểm tra mã của bạn (cũng thêm một repr cho đại diện lành mạnh của đối tượng của bạn):

class Student: 
    def __init__(self, name, age): 
     self.name = name 
     self.age = age 

    def __eq__(self, other): 
     return (self.name, self.age) == (other.name, other.age) 

    def __repr__(self): 
     return 'Student(name={}, age={})'.format(self.name, self.age) 

Sau đó, kiểm tra của bạn có thể trông như thế này:

@pytest.mark.parametrize('classinfo, expected', [ 
     (single_data(), [Student('alex', 20)]), 
     (double_data(), [Student('taylor', 23), Student('morgan', 25)]), 
]) 
def test_student(classinfo, expected): 
    assert classinfo.students == expected 
+0

Tôi đã thử sử dụng phương pháp ma thuật bình đẳng, nhưng trong mã thực tế của tôi, có khoảng 12 thuộc tính (một số trong đó khá dài), vì vậy tôi thấy khó so sánh cả hai và xác định chính xác vị trí của vấn đề. Tôi có nên cải thiện định dạng không? đây có phải là cách tốt hơn không? – Jake

+0

@jake là vấn đề tạo ra phương pháp bình đẳng hoặc thử nghiệm đơn vị? có vẻ như bạn đang sử dụng lớp để lưu trữ dữ liệu, hãy xem [tên tệp] (https://docs.python.org/3/library/collections.html#collections.namedtuple) hoặc [attrs] (https: // pypi.python.org/pypi/attrs/17.2.0) module, vì nó cung cấp các phương thức này miễn phí và nó phù hợp với trường hợp sử dụng này tốt hơn. – salparadise

2

Bạn có thể thêm một fixture trả về đối tượng của lớp đó và gọi cố định mà trước mọi thử nghiệm. Tôi đã thực hiện một số thay đổi và tạo một fixture get_object trong test_classlist.py trong khi classlist.py là như vậy.

get_object sẽ cung cấp cho bạn đối tượng của lớp đó và bạn có thể sử dụng đối tượng đó trong chức năng kiểm tra qua mô-đun request. Tôi đã gán đối tượng lớp đó trong request.instance.cobj. Bạn cũng có thể truy cập vào chức năng kiểm tra.

Điều tôi nhận được từ mô tả của mình là bạn muốn tạo đối tượng là ClassList. Nếu tôi không nhận được sai, giải pháp dưới đây sẽ làm việc cho bạn. Thử cái này.

import pytest 
from classlist import ClassList 

def single_data(): 
    text = 'name=alex$age=20$' 
    print text 
    return ClassList(text) 

def double_data(): 
    text = 'name=taylor$age=23$' \ 
      'name=morgan$age=25$' 
    return ClassList(text) 

@pytest.fixture 
def get_object(request): 
    classobj= request.getfuncargvalue('classinfo')() 
    request.instance.cobj = classobj 


class Test_clist: 

    @pytest.mark.parametrize('classinfo, expected', [ 
      (single_data, ['alex']), 
      (double_data, ['taylor', 'morgan']) #() removed 
    ]) 
    @pytest.mark.usefixtures('get_object') 
    def test_name(self,classinfo,expected,request): 
     result = [student.name for student in request.instance.cobj.students] 
     print result 
     print expected 
     assert result == expected 
+0

Nice. Đây là loại của tôi sau đó. Hai câu hỏi tiếp theo: 1. Không cần thêm phạm vi vào lịch thi đấu? 2. Tại sao các bài kiểm tra phải ở trong một lớp học để làm việc này? – Jake

+1

1.Nếu bạn không xác định phạm vi của lịch thi đấu, phạm vi mặc định sẽ là 'hàm' .. 2.Bằng cách đặt phương thức vào lớp, phạm vi của bài kiểm tra cũng sẽ là' hàm'. Để sử dụng 'yêu cầu' phạm vi của cả hai thử nghiệm và cố định phải giống nhau. –

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