|
| 1 | +from frappe.model.document import Document |
| 2 | +from frappe.model.meta import Meta |
| 3 | + |
| 4 | +def mock_field(**kwargs): |
| 5 | + """Creats a mock field definition. Used during unittesting to reduce framework calls and |
| 6 | + only test the smallest unit of work required. |
| 7 | + |
| 8 | + Params: |
| 9 | + kwargs: dict -> All fields you need to test against. Refer to doctype json file definitions. |
| 10 | +
|
| 11 | + Returns: |
| 12 | + A dict containing meta information of this field. |
| 13 | + """ |
| 14 | + |
| 15 | + meta = { |
| 16 | + "doctype": "DocField", |
| 17 | + "owner": "Administrator", |
| 18 | + "modified_by": "Administrator", |
| 19 | + "parentfield": "fields", |
| 20 | + "parenttype": "DocType" |
| 21 | + } |
| 22 | + |
| 23 | + if kwargs and len(kwargs) > 0: |
| 24 | + meta.update(kwargs) |
| 25 | + |
| 26 | + return meta |
| 27 | + |
| 28 | +def mock_meta(doctype, fields): |
| 29 | + """Creates a mock metadata for a given doctype. Used during unittesting to reduce framework calls and |
| 30 | + only test the smallest unit of work required.' |
| 31 | + |
| 32 | + Params: |
| 33 | + doctype: string -> The doctype to mock. |
| 34 | + fields: list -> A list of dict defining individual fields. Only define what you are testing against. |
| 35 | +
|
| 36 | + Returns: |
| 37 | + A Meta instance defining our mock doctype |
| 38 | + """ |
| 39 | + |
| 40 | + return Meta({ |
| 41 | + "doctype": "DocType", |
| 42 | + "name": doctype, |
| 43 | + "parent": None, |
| 44 | + "parentfield": None, |
| 45 | + "parenttype": None, |
| 46 | + "document_type": "Document", |
| 47 | + "fields": [ mock_field(doctype=doctype, **field) for field in fields ] |
| 48 | + }) |
| 49 | + |
| 50 | +def build_get_meta_side_effects(metas): |
| 51 | + """Returns a mock side effects method to be used as a side effect for the purpose of unit testing. |
| 52 | + This method replaces doctype metas for user defined ones. The returned method should be passed |
| 53 | + to a unittest.mock.MagicMock instance via patch or mock. This prevents database calls and enforces |
| 54 | + unit test isolation. |
| 55 | + |
| 56 | + Params: |
| 57 | + metas: list -> A list of Meta instances created with mock_meta() |
| 58 | +
|
| 59 | + Example: |
| 60 | + @patch('frappe.get_doc') |
| 61 | + @patch('frappe.get_meta') |
| 62 | + def test_fetch_quotation(self, get_meta, get_doc): |
| 63 | + # Mock Quotation |
| 64 | + get_doc.side_effect = [Document({ |
| 65 | + "doctype": "Quotation", |
| 66 | + "name": "test-quotation", |
| 67 | + "customer_name": "test-customer" |
| 68 | + })] |
| 69 | +
|
| 70 | + # Mock Quotation Metadata. Notice the limited set of fields. |
| 71 | + get_meta.side_effect = build_get_meta_side_effects( |
| 72 | + mock_meta("Quotation", fields=[ |
| 73 | + { "fieldname": "name", "fieldtype": "Data" }, |
| 74 | + { "fieldname": "customer_name", "fieldtype": "Link", "options": "Customer" } |
| 75 | + ]) |
| 76 | + ) |
| 77 | +
|
| 78 | + # Fetches the mocked quotation |
| 79 | + doc = frappe.get_doc("Quotation", "test-quotation") |
| 80 | +
|
| 81 | + # Then perform your tests |
| 82 | + get_doc.assert_called() |
| 83 | + get_meta.assert_called() |
| 84 | + self.assertTrue(doc.customer == "test-customer") |
| 85 | + """ |
| 86 | + |
| 87 | + def side_effect(doctype, cached=False): |
| 88 | + for meta in metas: |
| 89 | + if meta.name == doctype: |
| 90 | + return meta |
| 91 | + |
| 92 | + raise Exception("Unexpected get_meta doctype: {}".format(doctype)) |
| 93 | + |
| 94 | + return side_effect |
| 95 | + |
| 96 | +def build_get_doc_side_effect(docs): |
| 97 | + """Builds a side effect method to wrap frappe.get_doc method. This makes it |
| 98 | + convenient to store multiple mock documents during unittests""" |
| 99 | + |
| 100 | + def get_doc(doctype, name): |
| 101 | + for doc in docs: |
| 102 | + if doc.name == name: |
| 103 | + return doc |
| 104 | + |
| 105 | + raise Exception("Mock document not found: {}: {}".format(doctype, name)) |
| 106 | + |
| 107 | + return get_doc |
0 commit comments