Пример #1
0
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)
Пример #2
0
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