Try ERPNext Buy Support Partners Foundation

Help with Writing Tests

I’m starting with simple calculations and tests. I’m confused with writing tests.
I wrote a test_negative_hours
here are my modified files: test_time_log.py

# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt

import frappe
import unittest

from erpnext.projects.doctype.time_log.time_log import OverlapError
from erpnext.projects.doctype.time_log_batch.test_time_log_batch import *

class TestTimeLog(unittest.TestCase):
def test_duplication(self):
	frappe.db.sql("delete from `tabTime Log`")
	frappe.get_doc(frappe.copy_doc(test_records[0])).insert()

	ts = frappe.get_doc(frappe.copy_doc(test_records[0]))
	self.assertRaises(OverlapError, ts.insert)

	frappe.db.sql("delete from `tabTime Log`")

def test_negative_hours(self):
	frappe.db.sql("delete from `tabTime Log`")
	test_time_log = frappe.new_doc("Time Log")
	test_time_log.activity_type = "Communication"
	test_time_log.from_time = "2013-01-01 11:00:00.000000"
	test_time_log.to_time = "2013-01-01 10:00:00.000000"
	test_time_log.save()
	self.assertRaises(frappe.ValidationError, test_time_log.save)
	frappe.db.sql("delete from `tabTime Log`")

test_records = frappe.get_test_records('Time Log')
test_ignore = ["Time Log Batch", "Sales Invoice"]

then I ran test:

revant@revant-laptop:~/frappe-bench$ bench frappe --run_tests -d "Time Log"
.F.
======================================================================
FAIL: test_negative_hours (erpnext.projects.doctype.time_log.test_time_log.TestTimeLog)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/revant/frappe-bench/apps/erpnext/erpnext/projects/doctype/time_log/test_time_log.py", line 27, in test_negative_hours
    self.assertRaises(frappe.ValidationError, test_time_log.save)
AssertionError: ValidationError not raised

----------------------------------------------------------------------
Ran 3 tests in 0.455s

FAILED (failures=1)
<unittest.runner.TextTestResult run=3 errors=0 failures=1>

Then I modified time_log.py:

# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt

# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import cstr, comma_and


class OverlapError(frappe.ValidationError): pass

from frappe.model.document import Document

class TimeLog(Document):

	def validate(self):
	    self.set_status()
	    self.validate_overlap()
	    self.calculate_total_hours()
	
    def calculate_total_hours(self):
		from frappe.utils import time_diff_in_hours
	    self.hours = time_diff_in_hours(self.to_time, self.from_time)
	    # I added this if
	    if self.hours < 0:
			frappe.throw(_("\'From Time\' cannot be later than \'To Time\'"))

	def set_status(self):
	    self.status = {
			0: "Draft",
		    1: "Submitted",
		    2: "Cancelled"
	    }[self.docstatus or 0]

	    if self.time_log_batch:
			self.status="Batched for Billing"

		if self.sales_invoice:
		    self.status="Billed"

	def validate_overlap(self):
	    existing = frappe.db.sql_list("""select name from `tabTime Log` where owner=%s and
			(
			    (from_time between %s and %s) or
			    (to_time between %s and %s) or
			    (%s between from_time and to_time))
		    and name!=%s
		    and ifnull(task, "")=%s
		    and docstatus < 2""",
		    (self.owner, self.from_time, self.to_time, self.from_time,
				self.to_time, self.from_time, self.name or "No Name",
			    cstr(self.task)))

	    if existing:
			frappe.throw(_("This Time Log conflicts with {0}").format(comma_and(existing)), OverlapError)

	def before_cancel(self):
	    self.set_status()

	def before_update_after_submit(self):
	    self.set_status()

@frappe.whitelist()
def get_events(start, end):
	from frappe.widgets.reportview import build_match_conditions
	if not frappe.has_permission("Time Log"):
	    frappe.msgprint(_("No Permission"), raise_exception=1)

	match = build_match_conditions("Time Log")
	data = frappe.db.sql("""select name, from_time, to_time,
	    activity_type, task, project from `tabTime Log`
	    where from_time between '%(start)s' and '%(end)s' or to_time between '%(start)s' and '%(end)s'
	    %(match)s""" % {
			"start": start,
		    "end": end,
		    "match": match and (" and " + match) or ""
	    }, as_dict=True, update={"allDay": 0})

	for d in data:
	    d.title = d.name + ": " + (d.activity_type or "[Activity Type not set]")
	    if d.task:
			d.title += " for Task: " + d.task
	    if d.project:
			d.title += " for Project: " + d.project

	return data

I ran test again and got:

ValidationError: 'From Time' cannot be later than 'To Time'

----------------------------------------------------------------------
Ran 3 tests in 0.471s

FAILED (errors=1)
<unittest.runner.TextTestResult run=3 errors=1 failures=0>

Now I’m confused,
It doesnt let me save negative hours in Time log as expected.
How to pass the test? I’m not understanding writing test.

delete the line test_time_log.save() in test_negative_hours function. It should work.