class TestTax(unittest.TestCase): """ Tests the `Tax` class. """ # We save a number of attributes for convenience in testing later # on. We could refactor, but it would complicate the tests, which # would be worse. # pylint: disable=too-many-instance-attributes def setUp(self): self.initial_year = 2000 # Build 100 years of inflation adjustments with steadily growing # adjustment factors. First, pick a nice number (ideally a power # of 2 to avoid float precision issues); inflation_adjustment # will grow by adding the inverse (1/n) of this number annually growth_factor = 32 year_range = range(self.initial_year, self.initial_year + 100) self.inflation_adjustments = { year: 1 + (year - self.initial_year) / growth_factor for year in year_range } # For convenience, store the year where inflation has doubled # the nominal value of money self.double_year = self.initial_year + growth_factor # Build some brackets with nice round numbers: self.tax_brackets = {self.initial_year: {0: 0.1, 100: 0.2, 10000: 0.3}} # For convenience, build a sorted, type-converted array of # each of the tax bracket thresholds: self.brackets = sorted({ key: self.tax_brackets[self.initial_year][key] for key in self.tax_brackets[self.initial_year].keys() }) # For convenience in testing, build an accum dict that # corresponds to the tax brackets above. self.accum = {self.initial_year: {0: 0, 100: 10, 10000: 1990}} self.personal_deduction = {self.initial_year: 100} self.credit_rate = {self.initial_year: 0.1} self.tax = Tax(self.tax_brackets, inflation_adjust=self.inflation_adjustments, personal_deduction=self.personal_deduction, credit_rate=self.credit_rate) # Set up a simple person with no account-derived taxable income self.person = Person(self.initial_year, "Tester", self.initial_year - 25, retirement_date=self.initial_year + 40, gross_income=0) # Set up two people, spouses, on which to do more complex tests self.person1 = Person(self.initial_year, "Tester 1", self.initial_year - 20, retirement_date=self.initial_year + 45, gross_income=100000) self.person2 = Person(self.initial_year, "Tester 2", self.initial_year - 22, retirement_date=self.initial_year + 43, gross_income=50000) # Give the first person two accounts, one taxable and one # tax-deferred. Withdraw the entirety from the taxable account, # so that we don't need to worry about tax on unrealized growth: self.taxable_account1 = TaxableAccount(owner=self.person1, acb=0, balance=50000, rate=0.05, nper=1) self.taxable_account1.add_transaction(-50000, when='start') self.rrsp = RRSP(owner=self.person1, inflation_adjust=self.inflation_adjustments, contribution_room=0, balance=10000, rate=0.05, nper=1) # Employment income is fully taxable, and only half of capital # gains (the income from the taxable account) is taxable: self.person1_taxable_income = (self.person1.gross_income + self.taxable_account1.balance / 2) # Give the second person two accounts, one taxable and one # non-taxable. Withdraw the entirety from the taxable account, # so that we don't need to worry about tax on unrealized growth, # and withdraw a bit from the non-taxable account (which should # have no effect on taxable income): self.taxable_account2 = TaxableAccount(owner=self.person2, acb=0, balance=20000, rate=0.05, nper=1) self.taxable_account2.add_transaction(-20000, when='start') self.tfsa = TFSA(owner=self.person2, balance=50000, rate=0.05, nper=1) self.tfsa.add_transaction(-20000, when='start') # Employment income is fully taxable, and only half of capital # gains (the income from the taxable account) is taxable: self.person2_taxable_income = (self.person2.gross_income + self.taxable_account2.balance / 2) def setUp_decimal(self): self.initial_year = 2000 # Build 100 years of inflation adjustments with steadily growing # adjustment factors. First, pick a nice number (ideally a power # of 2 to avoid float precision issues); inflation_adjustment # will grow by adding the inverse (1/n) of this number annually growth_factor = 32 year_range = range(self.initial_year, self.initial_year + 100) self.inflation_adjustments = { year: 1 + (year - self.initial_year) / growth_factor for year in year_range } # For convenience, store the year where inflation has doubled # the nominal value of money self.double_year = self.initial_year + growth_factor # Build some brackets with nice round numbers: self.tax_brackets = { self.initial_year: { Decimal(0): Decimal('0.1'), Decimal('100'): Decimal('0.2'), Decimal('10000'): Decimal('0.3') } } # For convenience, build a sorted, type-converted array of # each of the tax bracket thresholds: self.brackets = sorted({ Decimal(key): self.tax_brackets[self.initial_year][key] for key in self.tax_brackets[self.initial_year].keys() }) # For convenience in testing, build an accum dict that # corresponds to the tax brackets above. self.accum = { self.initial_year: { Decimal(0): Decimal('0'), Decimal('100'): Decimal('10'), Decimal('10000'): Decimal('1990') } } self.personal_deduction = {self.initial_year: Decimal('100')} self.credit_rate = {self.initial_year: Decimal('0.1')} self.tax = Tax(self.tax_brackets, inflation_adjust=self.inflation_adjustments, personal_deduction=self.personal_deduction, credit_rate=self.credit_rate) # Set up a simple person with no account-derived taxable income self.person = Person(self.initial_year, "Tester", self.initial_year - 25, retirement_date=self.initial_year + 40, gross_income=Decimal(0)) # Set up two people, spouses, on which to do more complex tests self.person1 = Person(self.initial_year, "Tester 1", self.initial_year - 20, retirement_date=self.initial_year + 45, gross_income=Decimal(100000)) self.person2 = Person(self.initial_year, "Tester 2", self.initial_year - 22, retirement_date=self.initial_year + 43, gross_income=Decimal(50000)) # Give the first person two accounts, one taxable and one # tax-deferred. Withdraw the entirety from the taxable account, # so that we don't need to worry about tax on unrealized growth: self.taxable_account1 = TaxableAccount(owner=self.person1, acb=Decimal(0), balance=Decimal(50000), rate=Decimal('0.05'), nper=Decimal(1)) self.taxable_account1.add_transaction(Decimal(-50000), when='start') self.rrsp = RRSP(owner=self.person1, inflation_adjust=self.inflation_adjustments, contribution_room=Decimal(0), balance=Decimal(10000), rate=Decimal('0.05'), nper=Decimal(1)) # Employment income is fully taxable, and only half of capital # gains (the income from the taxable account) is taxable: self.person1_taxable_income = Decimal(self.person1.gross_income + self.taxable_account1.balance / 2) # Give the second person two accounts, one taxable and one # non-taxable. Withdraw the entirety from the taxable account, # so that we don't need to worry about tax on unrealized growth, # and withdraw a bit from the non-taxable account (which should # have no effect on taxable income): self.taxable_account2 = TaxableAccount(owner=self.person2, acb=Decimal(0), balance=Decimal(20000), rate=Decimal('0.05'), nper=Decimal(1)) self.taxable_account2.add_transaction(Decimal(-20000), when='start') self.tfsa = TFSA(owner=self.person2, balance=Decimal(50000), rate=Decimal('0.05'), nper=Decimal(1)) self.tfsa.add_transaction(Decimal(-20000), when='start') # Employment income is fully taxable, and only half of capital # gains (the income from the taxable account) is taxable: self.person2_taxable_income = Decimal(self.person2.gross_income + self.taxable_account2.balance / 2) def test_init_optional(self): """ Test Tax.__init__ with all arguments, including optional. """ tax = Tax(self.tax_brackets, inflation_adjust=self.inflation_adjustments, personal_deduction=self.personal_deduction, credit_rate=self.credit_rate) for year in self.tax_brackets: self.assertEqual(tax.tax_brackets(year), self.tax_brackets[year]) self.assertEqual(tax.accum(year), self.accum[year]) self.assertEqual(tax.personal_deduction(year), self.personal_deduction[year]) self.assertEqual(tax.credit_rate(year), self.credit_rate[year]) self.assertTrue(callable(tax.inflation_adjust)) def test_init_basic(self): """ Test Tax.__init__ with only mandatory arguments. """ tax = Tax(self.tax_brackets, inflation_adjust=self.inflation_adjustments) for year in self.tax_brackets: self.assertEqual(tax.tax_brackets(year), self.tax_brackets[year]) self.assertEqual(tax.accum(year), self.accum[year]) self.assertTrue(callable(tax.inflation_adjust)) self.assertEqual(tax.personal_deduction(self.initial_year), 0) self.assertEqual(tax.credit_rate(self.initial_year), 1) def test_income_0_money(self): """ Call Test on $0 income. """ # $0 should return $0 in tax owing. This is the easiest test. income = 0 self.assertEqual(self.tax(income, self.initial_year), 0) def test_income_0_person(self): """ Test tax on a person with $0 income. """ # $0 should return $0 in tax owing. This is the easiest test. self.person.gross_income = 0 self.assertEqual(self.tax(self.person, self.initial_year), 0) def test_income_under_deduction(self): """ Test tax on person with income under personal deduction. """ self.person.gross_income = ( self.personal_deduction[self.initial_year] / 2) # Should return $0 self.assertEqual(self.tax(self.person, self.initial_year), 0) def test_income_at_deduction(self): """ Call Test on income equal to the personal deduction. """ self.person.gross_income = self.personal_deduction[self.initial_year] # Should return $0 self.assertEqual(self.tax(self.person, self.initial_year), 0) def test_income_in_bracket_1_money(self): """ Call Test on income mid-way into the lowest tax bracket. """ # NOTE: brackets[0] is $0; we need something between brackets[0] # and brackets[1]) income = self.brackets[1] / 2 self.assertEqual( self.tax(income, self.initial_year), income * self.tax_brackets[self.initial_year][self.brackets[0]]) def test_income_in_bracket_1_person(self): """ Call Test on income mid-way into the lowest tax bracket. """ # NOTE: brackets[0] is $0; we need something between brackets[0] # and brackets[1]) self.person.gross_income = (self.brackets[1] / 2 + self.personal_deduction[self.initial_year]) self.assertEqual( self.tax(self.person, self.initial_year), (self.person.gross_income - self.personal_deduction[self.initial_year]) * self.tax_brackets[self.initial_year][self.brackets[0]]) def test_income_at_bracket_1_money(self): """ Call Test on income equal to the lowest tax bracket. """ # Try a value that's at the limit of the lowest tax # bracket (NOTE: brackets are inclusive, so brackets[1] is # entirely taxed at the rate associated with brackets[0]) income = self.brackets[1] self.assertEqual(self.tax(income, self.initial_year), self.accum[self.initial_year][self.brackets[1]]) def test_income_at_bracket_1_person(self): """ Call Test on income equal to the lowest tax bracket. """ # Try a value that's at the limit of the lowest tax # bracket (NOTE: brackets are inclusive, so brackets[1] is # entirely taxed at the rate associated with brackets[0]) self.person.gross_income = (self.brackets[1] + self.personal_deduction[self.initial_year]) self.assertEqual(self.tax(self.person, self.initial_year), self.accum[self.initial_year][self.brackets[1]]) def test_income_in_bracket_2_money(self): """ Call Test on income mid-way into the second tax bracket. """ # Find a value that's mid-way into the next (second) bracket. # Assuming a person deduction of $100 and tax rates bounded at # $0, $100 and $10000 with 10%, 20%, and 30% rates, this gives: # Tax on first $100: $0 # Tax on next $100: $10 # Tax on remaining: 20% of remaining # For a $5150 amount, this works out to tax of $1000. income = (self.brackets[1] + self.brackets[2]) / 2 self.assertEqual( self.tax(income, self.initial_year), self.accum[self.initial_year][self.brackets[1]] + ((self.brackets[1] + self.brackets[2]) / 2 - self.brackets[1]) * self.tax_brackets[self.initial_year][self.brackets[1]]) def test_income_in_bracket_2_person(self): """ Call Test on income mid-way into the second tax bracket. """ # Find a value that's mid-way into the next (second) bracket. # Assuming a person deduction of $100 and tax rates bounded at # $0, $100 and $10000 with 10%, 20%, and 30% rates, this gives: # Tax on first $100: $0 # Tax on next $100: $10 # Tax on remaining: 20% of remaining # For a $5150 amount, this works out to tax of $1000. self.person.gross_income = ((self.brackets[1] + self.brackets[2]) / 2 + self.personal_deduction[self.initial_year]) target = (self.accum[self.initial_year][self.brackets[1]] + ( (self.brackets[1] + self.brackets[2]) / 2 - self.brackets[1]) * self.tax_brackets[self.initial_year][self.brackets[1]]) self.assertEqual(self.tax(self.person, self.initial_year), target) def test_income_at_bracket_2_money(self): """ Call Test on income equal to the second tax bracket. """ # Try again for a value that's at the limit of the second tax # bracket (NOTE: brackets are inclusive, so brackets[2] is # entirely taxed at the rate associated with brackets[1]) income = self.brackets[2] self.assertEqual( self.tax(income, self.initial_year), self.accum[self.initial_year][self.brackets[1]] + (self.brackets[2] - self.brackets[1]) * self.tax_brackets[self.initial_year][self.brackets[1]]) def test_income_at_bracket_2_person(self): """ Call Test on income equal to the second tax bracket. """ # Try again for a value that's at the limit of the second tax # bracket (NOTE: brackets are inclusive, so brackets[2] is # entirely taxed at the rate associated with brackets[1]) self.person.gross_income = (self.brackets[2] + self.personal_deduction[self.initial_year]) self.assertEqual( self.tax(self.person, self.initial_year), self.accum[self.initial_year][self.brackets[1]] + (self.brackets[2] - self.brackets[1]) * self.tax_brackets[self.initial_year][self.brackets[1]]) def test_income_in_bracket_3_money(self): """ Call Test on income in the highest tax bracket. """ # Find a value that's somewhere in the highest (unbounded) bracket. bracket = max(self.brackets) income = bracket * 2 self.assertEqual( self.tax(income, self.initial_year), self.accum[self.initial_year][bracket] + bracket * self.tax_brackets[self.initial_year][bracket]) def test_income_in_bracket_3_person(self): """ Call Test on income in the highest tax bracket. """ # Find a value that's somewhere in the highest (unbounded) bracket. bracket = max(self.brackets) self.person.gross_income = (bracket * 2 + self.personal_deduction[self.initial_year]) self.assertEqual( self.tax(self.person, self.initial_year), self.accum[self.initial_year][bracket] + bracket * self.tax_brackets[self.initial_year][bracket]) def test_taxpayer_single(self): """ Call test on a single taxpayer. """ # The tax paid on the person's income should be the same as if # we calculated the tax directly on the money itself (after # accounting for the personal deduction amount) self.assertEqual( self.tax(self.person1, self.initial_year), self.tax( self.person1_taxable_income - self.personal_deduction[self.initial_year], self.initial_year)) # We should get a similar result on the other person: self.assertEqual( self.tax(self.person2, self.initial_year), self.tax( self.person2_taxable_income - self.personal_deduction[self.initial_year], self.initial_year)) def test_taxpayer_single_set(self): """ Call test on a set with a single taxpayer member. """ # Try with a single-member set; should return the same as # it would if calling on the person directly. self.assertEqual(self.tax({self.person1}, self.initial_year), self.tax(self.person1, self.initial_year)) def test_taxpayer_set(self): """ Call Test on a set of two non-spouse taxpayers. """ # The two test people are set up as spouses; we need to split # them up. self.person1.spouse = None self.person2.spouse = None test_result = (self.tax({self.person1, self.person2}, self.initial_year)) test_target = (self.tax(self.person1, self.initial_year) + self.tax(self.person2, self.initial_year)) self.assertEqual(test_result, test_target) def test_taxpayer_spouses(self): """ Call Test on a set of two spouse taxpayers. """ # NOTE: This test is vulnerable to breakage if special tax # credits get implemented for spouses. Watch out for that. self.assertEqual( self.tax({self.person1, self.person2}, self.initial_year), self.tax(self.person1, self.initial_year) + self.tax(self.person2, self.initial_year)) def test_inflation_adjust(self): """ Call Test on a future year with inflation effects. """ # Start with a baseline result in initial_year. Then confirm # that the tax owing on twice that amount in double_year should # be exactly double the tax owing on the baseline result. # (Anything else suggests that something is not being inflation- # adjusted properly, e.g. a bracket or a deduction) double_tax = self.tax(self.person1_taxable_income * 2, self.double_year) single_tax = self.tax(self.person1_taxable_income, self.initial_year) self.assertEqual(single_tax * 2, double_tax) def test_payment_timing(self): """ Tests `payment_timing` property. """ # `payment_timing` should have exactly one timing: 0 self.tax.payment_timing = 'start' self.assertEqual(set(self.tax.payment_timing), {0}) def test_refund_timing(self): """ Tests `refund_timing` property. """ # `refund_timing` should have exactly one timing: 0 self.tax.refund_timing = 'start' self.assertEqual(set(self.tax.refund_timing), {0}) def test_decimal(self): """ Call Test with Decimal values. """ # Convert values to Decimal: self.setUp_decimal() # This test is based on test_income_in_bracket_2_person self.person.gross_income = ((self.brackets[1] + self.brackets[2]) / 2 + self.personal_deduction[self.initial_year]) target = (self.accum[self.initial_year][self.brackets[1]] + ( (self.brackets[1] + self.brackets[2]) / 2 - self.brackets[1]) * self.tax_brackets[self.initial_year][self.brackets[1]]) self.assertEqual(self.tax(self.person, self.initial_year), target)
class TestTaxCanada(unittest.TestCase): """ Tests TaxCanada. """ def setUp(self): """ Sets up mutable variables for each test call. """ # Set up constants: self.initial_year = 2000 self.constants = constants.ConstantsCanada() # Modify constants to make math easier: # Build some brackets with nice round numbers: self.constants.TAX_BRACKETS = { 'Federal': { self.initial_year: { 0: 0.1, 100: 0.2, 10000: 0.3 } }, 'BC': { self.initial_year: { 0: 0.25, 1000: 0.5, 100000: 0.75 } } } self.constants.TAX_PERSONAL_DEDUCTION = { 'Federal': { self.initial_year: 100 }, 'BC': { self.initial_year: 1000 } } self.constants.TAX_CREDIT_RATE = { 'Federal': { self.initial_year: 0.1 }, 'BC': { self.initial_year: 0.25 } } self.constants.TAX_PENSION_CREDIT = { 'Federal': { self.initial_year: 100 }, 'BC': { self.initial_year: 1000 } } # It's convenient (and accurate!) to use the same values # for the spousal amount and the personal deduction: self.constants.TAX_SPOUSAL_AMOUNT = ( self.constants.TAX_PERSONAL_DEDUCTION) # Build 100 years of inflation adjustments. growth_factor = 32 year_range = range(self.initial_year, self.initial_year + 100) self.inflation_adjustments = { year: 1 + (year - self.initial_year) / growth_factor for year in year_range } # Set to default province: self.province = 'BC' self.tax = TaxCanada(self.inflation_adjustments, province='BC', constants=self.constants) # Set up some people to test on: # Person1 makes $100,000/yr, has a taxable account with $500,000 # taxable income, and an RRSP with $500,000 in taxable income. self.person1 = Person(self.initial_year, "Tester 1", self.initial_year - 20, retirement_date=self.initial_year + 45, gross_income=100000) self.taxable_account1 = TaxableAccount(owner=self.person1, acb=0, balance=1000000, rate=0.05, nper=1) self.taxable_account1.add_transaction(-1000000, when='start') # NOTE: by using an RRSP here, a pension income tax credit will # be applied by TaxCanadaJurisdiction. Be aware of this if you # want to test this output against a generic Tax object with # Canadian brackets. self.rrsp = RRSP(self.person1, inflation_adjust=self.inflation_adjustments, contribution_room=0, balance=500000, rate=0.05, nper=1, constants=self.constants) self.rrsp.add_transaction(-500000, when='start') # Person2 makes $50,000/yr and has a taxable account with # $5000 taxable income. self.person2 = Person(self.initial_year, "Tester 2", self.initial_year - 18, retirement_date=self.initial_year + 47, gross_income=50000) self.taxable_account2 = TaxableAccount(owner=self.person2, acb=0, balance=10000, rate=0.05, nper=1) self.taxable_account2.add_transaction(-10000, when='start') def setUp_decimal(self): # pylint: disable=invalid-name """ Sets up mutable variables for each test call. """ # Set up constants: self.initial_year = 2000 self.constants = constants.ConstantsCanada(high_precision=Decimal) # Modify constants to make math easier: # Build some brackets with nice round numbers: self.constants.TAX_BRACKETS = { 'Federal': { self.initial_year: { Decimal(0): Decimal('0.1'), Decimal(100): Decimal('0.2'), Decimal(10000): Decimal('0.3') } }, 'BC': { self.initial_year: { Decimal(0): Decimal('0.25'), Decimal(1000): Decimal('0.5'), Decimal(100000): Decimal('0.75') } } } self.constants.TAX_PERSONAL_DEDUCTION = { 'Federal': { self.initial_year: Decimal('100') }, 'BC': { self.initial_year: Decimal('1000') } } self.constants.TAX_CREDIT_RATE = { 'Federal': { self.initial_year: Decimal('0.1') }, 'BC': { self.initial_year: Decimal('0.25') } } self.constants.TAX_PENSION_CREDIT = { 'Federal': { self.initial_year: Decimal('100') }, 'BC': { self.initial_year: Decimal('1000') } } # It's convenient (and accurate!) to use the same values # for the spousal amount and the personal deduction: self.constants.TAX_SPOUSAL_AMOUNT = ( self.constants.TAX_PERSONAL_DEDUCTION) # Build 100 years of inflation adjustments. growth_factor = Decimal(32) year_range = range(self.initial_year, self.initial_year + 100) self.inflation_adjustments = { year: 1 + (year - self.initial_year) / growth_factor for year in year_range } # Set to default province: self.province = 'BC' self.tax = TaxCanada(self.inflation_adjustments, province='BC', constants=self.constants) # Set up some people to test on: # Person1 makes $100,000/yr, has a taxable account with $500,000 # taxable income, and an RRSP with $500,000 in taxable income. self.person1 = Person(self.initial_year, "Tester 1", self.initial_year - 20, retirement_date=self.initial_year + 45, gross_income=100000) self.taxable_account1 = TaxableAccount(owner=self.person1, acb=0, balance=Decimal(1000000), rate=Decimal('0.05'), nper=1) self.taxable_account1.add_transaction(-Decimal(1000000), when='start') # NOTE: by using an RRSP here, a pension income tax credit will # be applied by TaxCanadaJurisdiction. Be aware of this if you # want to test this output against a generic Tax object with # Canadian brackets. self.rrsp = RRSP(self.person1, inflation_adjust=self.inflation_adjustments, contribution_room=0, balance=Decimal(500000), rate=Decimal('0.05'), nper=1, constants=self.constants) self.rrsp.add_transaction(-Decimal(500000), when='start') # Person2 makes $50,000/yr and has a taxable account with # $5000 taxable income. self.person2 = Person(self.initial_year, "Tester 2", self.initial_year - 18, retirement_date=self.initial_year + 47, gross_income=50000) self.taxable_account2 = TaxableAccount(owner=self.person2, acb=0, balance=Decimal(10000), rate=Decimal('0.05'), nper=1) self.taxable_account2.add_transaction(-Decimal(10000), when='start') def test_init_federal(self): """ Test TaxCanada.__init__ for federal jurisdiction. """ # There's some type-conversion going on, so test the Decimal- # valued `amount` of the Tax's tax bracket's keys against the # Decimal key object of the Constants tax brackets. tax = TaxCanada(self.inflation_adjustments, self.province, constants=self.constants) for year in self.constants.TAX_BRACKETS['Federal']: self.assertEqual(tax.federal_tax.tax_brackets(year), self.constants.TAX_BRACKETS['Federal'][year]) self.assertEqual( tax.federal_tax.personal_deduction(year), self.constants.TAX_PERSONAL_DEDUCTION['Federal'][year]) self.assertEqual(tax.federal_tax.credit_rate(year), self.constants.TAX_CREDIT_RATE['Federal'][year]) self.assertTrue(callable(tax.federal_tax.inflation_adjust)) # Test that the default timings for CRA refunds/payments have # been set: self.assertEqual(set(tax.payment_timing), {self.constants.TAX_PAYMENT_TIMING}) self.assertEqual(set(tax.refund_timing), {self.constants.TAX_REFUND_TIMING}) def test_init_provincial(self): """ Test TaxCanada.__init__ for provincial jurisdiction. """ tax = TaxCanada(self.inflation_adjustments, self.province, constants=self.constants) for year in self.constants.TAX_BRACKETS[self.province]: self.assertEqual( tax.provincial_tax.tax_brackets(year), { bracket: value for bracket, value in self.constants.TAX_BRACKETS[ self.province][year].items() }) self.assertEqual( tax.provincial_tax.personal_deduction(year), self.constants.TAX_PERSONAL_DEDUCTION[self.province][year]) self.assertEqual( tax.provincial_tax.credit_rate(year), self.constants.TAX_CREDIT_RATE[self.province][year]) self.assertTrue(callable(tax.provincial_tax.inflation_adjust)) def test_init_min_args(self): """ Test init when Omitting optional arguments. """ tax = TaxCanada(self.inflation_adjustments, constants=self.constants) for year in self.constants.TAX_BRACKETS[self.province]: self.assertEqual( tax.provincial_tax.tax_brackets(year), { bracket: value for bracket, value in self.constants.TAX_BRACKETS[ self.province][year].items() }) self.assertEqual( tax.provincial_tax.personal_deduction(year), self.constants.TAX_PERSONAL_DEDUCTION[self.province][year]) self.assertEqual( tax.provincial_tax.credit_rate(year), self.constants.TAX_CREDIT_RATE[self.province][year]) self.assertTrue(callable(tax.provincial_tax.inflation_adjust)) def test_call_money(self): """ Test TaxCanada.__call__ on Decimal input """ taxable_income = 100000 self.assertEqual( self.tax(taxable_income, self.initial_year), self.tax.federal_tax(taxable_income, self.initial_year) + self.tax.provincial_tax(taxable_income, self.initial_year)) def test_call_person(self): """ Test TaxCanada.__call__ on one Person input """ self.assertEqual( self.tax(self.person1, self.initial_year), self.tax.federal_tax(self.person1, self.initial_year) + self.tax.provincial_tax(self.person1, self.initial_year)) def test_call_person_set(self): """ Test TaxCanada.__call__ on a one-Person set input """ # Should get the same result as for a setless Person: self.assertEqual(self.tax({self.person1}, self.initial_year), self.tax(self.person1, self.initial_year)) def test_call_people(self): """ Test TaxCanada.__call__ on a set of multiple people. """ # The people are unrelated, so should get a result which is # just the sum of their tax treatments. self.assertEqual( self.tax({self.person1, self.person2}, self.initial_year), self.tax.federal_tax({self.person1, self.person2}, self.initial_year) + self.tax.provincial_tax({self.person1, self.person2}, self.initial_year)) def test_spousal_tax_credit(self): """ Test spousal tax credit behaviour. """ # Ensure person 2's net income is less than the federal spousal # amount: spousal_amount = ( self.constants.TAX_SPOUSAL_AMOUNT['Federal'][self.initial_year]) shortfall = spousal_amount / 2 deduction = self.tax.federal_tax.deduction(self.person2, self.initial_year) self.person2.gross_income = deduction + spousal_amount - shortfall # Ensure that there is no taxable income for person2 beyond the # above (to stay under spousal amount): self.taxable_account2.owner = self.person1 # Get a tax treatment baseline for unrelated people: baseline_tax = self.tax.federal_tax({self.person1, self.person2}, self.initial_year) # Wed the two people in holy matrimony: self.person1.spouse = self.person2 # Now determine total tax liability federally: spousal_tax = self.tax.federal_tax({self.person1, self.person2}, self.initial_year) # Tax should be reduced (relative to baseline) by the shortfall # of person2's income (relative to the spousal amount, after # applying deductions), scaled down by the credit rate. # That is, for every dollar that person2 earns _under_ the # spousal amount, tax is reduced by (e.g.) 15 cents (assuming # a credit rate of 15%) target = baseline_tax - ( shortfall * self.tax.federal_tax.credit_rate(self.initial_year)) # The different between these scenarios should be equal to # the amount of the spousal tax credit: self.assertEqual(spousal_tax, target) def test_pension_tax_credit(self): """ Test pension tax credit behaviour. """ # TODO Implement pension tax credit, then test it. pass