Skip to content

Instantly share code, notes, and snippets.

@rickardlindberg
Created July 23, 2010 11:50
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save rickardlindberg/487345 to your computer and use it in GitHub Desktop.
TDD Calculator
# Response to http://misko.hevery.com/2009/11/17/how-to-get-started-with-tdd/
import unittest
class CalculatorTest(unittest.TestCase):
def setUp(self):
class MockView(object):
def __init__(self):
self.exp = ""
def set_expression(self, exp):
self.exp = exp
def get_expression(self):
return self.exp
self.view = MockView()
self.calculator = Calculator(self.view)
def testShouldEchoSingleNumberPress(self):
self._pushSequenceAndAssertEquals("1", "1")
def testShouldConcatenateNumberPresses(self):
self._pushSequenceAndAssertEquals("139", "139")
def testShouldDisplayNotEvaluatedExpression(self):
self._pushSequenceAndAssertEquals("1+3", "1+3")
def testShouldEvaluateExpressionOnEqualPress(self):
self._pushSequenceAndAssertEquals("1+3=", "4")
def testShouldEvaluateExpressionOnOperator(self):
self._pushSequenceAndAssertEquals("1+3+", "4+")
def testShouldHandleDecimalInput(self):
self._pushSequenceAndAssertEquals("38.7", "38.7")
def testShouldIngoreMultipleDecimalPoints(self):
self._pushSequenceAndAssertEquals("38.7..8..", "38.78")
def testShouldHandleAllBinOps(self):
self._pushSequenceAndAssertEquals("1+2*4/2-2=", "4")
def testShouldNotPrintIntAsFloat(self):
self._pushSequenceAndAssertEquals("4=", "4")
def testShouldRoundFloatToTwoDecimals(self):
self._pushSequenceAndAssertEquals("1.251=", "1.25")
def testShouldAddDecimalNumbers(self):
self._pushSequenceAndAssertEquals("4.5+3.15=", "7.65")
def testShouldIgnoreOperatorBeforeNumber(self):
self._pushSequenceAndAssertEquals("+*34", "34")
def testShouldIgnoreOperatorAfterOperator(self):
self._pushSequenceAndAssertEquals("4/*-=", "4/")
def testShouldDisplayNegativeNumber(self):
self._pushSequenceAndAssertEquals("4-6=", "-2")
def _pushSequenceAndAssertEquals(self, sequence, shouldEqual):
for char in sequence:
self.calculator.push(char)
self.assertEquals(shouldEqual, self.view.get_expression())
class Calculator(object):
def __init__(self, view):
self.view = view
self.tokens = [""]
def push(self, char):
if char in "=+-*/":
if self._is_last_token_number():
self._eval()
if char != "=":
self.tokens.append(char)
self.tokens.append("")
elif char != "." or "." not in self.tokens[-1]:
self.tokens[-1] += char
self._display_expression()
def _is_last_token_number(self):
try:
float(self.tokens[-1])
return True
except Exception:
return False
def _eval(self):
if len(self.tokens) == 3:
self._eval_binop()
elif len(self.tokens) == 1:
number, is_float = self._pop_number()
self._append_number(number, is_float)
def _pop_number(self):
numstr = self.tokens.pop(0)
if "." in numstr:
return (float(numstr), True)
return (int(numstr), False)
def _append_number(self, number, is_float):
rounded_number = number
if is_float:
rounded_number = round(number, 2)
self.tokens.append(str(rounded_number))
def _eval_binop(self):
op_map = {
"+": lambda x, y: x + y,
"-": lambda x, y: x - y,
"*": lambda x, y: x * y,
"/": lambda x, y: x / y,
}
n1, n1_is_float = self._pop_number()
op = self.tokens.pop(0)
n2, n2_is_float = self._pop_number()
res = op_map[op](n1, n2)
res_is_float = n1_is_float or n2_is_float
self._append_number(res, res_is_float)
def _display_expression(self):
self.view.set_expression("".join(self.tokens))
if __name__ == '__main__':
unittest.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment