Skip to content

Commit a7985de

Browse files
vahid-ahmadiclaudeMaxGhenis
authored
Add unit tests for CPS marriage tax reforms (#737) (#1642)
* Add unit tests for CPS marriage tax reforms (#737) Expose module-level Reform instances (`expanded_ma_reform`, `marriage_neutral_it_reform`) so YAML tests can reference them by dotted import path, matching the existing pattern used by `scottish_child_payment_reform`. Both are built with no child-age or education filters, so they apply to every married couple in tests. Add three initial test cases covering the reform mechanics: - Expanded MA: a single higher-rate earner has no Marriage Allowance even with the reform active (no spouse to transfer from). - Expanded MA: a married higher-rate earner with a low-earning spouse receives Marriage Allowance under the reform (normally ineligible under ITA 2007 s. 55B because they're over the basic rate). With take-up forced to 1.0, the expected transfer is £1,260 — low earner's unused PA capped at PA × ma_rate, rounded up to the £10 increment. - Marriage-neutral IT: a single person's adjusted net income is unchanged because `has_spouse` is False and the optimal-split branch collapses to the original income. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Cover marriage-neutral income splitting --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Max Ghenis <mghenis@gmail.com>
1 parent 077ae19 commit a7985de

3 files changed

Lines changed: 110 additions & 0 deletions

File tree

changelog.d/737.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Add unit tests for the two CPS marriage tax reforms (`expanded_ma_reform` and `marriage_neutral_it_reform`) and expose module-level Reform instances for them so they can be referenced from YAML tests by dotted import path. Tests cover: Marriage Allowance denied to a single higher-rate earner under the expanded-MA reform, Marriage Allowance granted to a higher-rate earner with a low-earning spouse under the expanded-MA reform, and adjusted net income unchanged for a single person under the marriage-neutral IT reform.

policyengine_uk/reforms/cps/marriage_tax_reforms.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,10 @@ def create_marriage_tax_reform(parameters, period):
227227
return it_reform
228228
else:
229229
return None
230+
231+
232+
# Module-level reform instances for use in YAML tests (reference by dotted import path).
233+
# These build the reform classes with no child-age / education conditions, so the reform
234+
# applies to every married couple regardless of child presence.
235+
expanded_ma_reform = create_expanded_ma_reform()
236+
marriage_neutral_it_reform = create_marriage_neutral_income_tax_reform()
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# CPS Marriage Tax Reforms (Centre for Policy Studies)
2+
# Covers two structural reforms:
3+
# - expanded_ma_reform: extends Marriage Allowance eligibility to higher/additional
4+
# band earners (normally ineligible under ITA 2007 s. 55B).
5+
# - marriage_neutral_it_reform: allows married couples to have income tax computed
6+
# on equally split income if that yields a lower combined liability.
7+
#
8+
# The module-level reform instances are built with no child-age / education filters,
9+
# so they apply to every married couple in these tests.
10+
11+
- name: Expanded MA — single higher-rate earner still has no Marriage Allowance
12+
period: 2024
13+
absolute_error_margin: 1
14+
reforms:
15+
- policyengine_uk.reforms.cps.marriage_tax_reforms.expanded_ma_reform
16+
input:
17+
gov.hmrc.income_tax.allowances.marriage_allowance.takeup_rate: 1.0
18+
people:
19+
adult:
20+
age: 35
21+
employment_income: 60_000
22+
benunits:
23+
benunit:
24+
members: [adult]
25+
is_married: false
26+
households:
27+
household:
28+
members: [adult]
29+
output:
30+
marriage_allowance: 0
31+
32+
- name: Expanded MA — married higher-rate earner gets Marriage Allowance from low-earner spouse
33+
period: 2024
34+
absolute_error_margin: 10
35+
reforms:
36+
- policyengine_uk.reforms.cps.marriage_tax_reforms.expanded_ma_reform
37+
input:
38+
gov.hmrc.income_tax.allowances.marriage_allowance.takeup_rate: 1.0
39+
people:
40+
high_earner:
41+
age: 35
42+
employment_income: 60_000
43+
low_earner:
44+
age: 33
45+
employment_income: 10_000
46+
benunits:
47+
benunit:
48+
members: [high_earner, low_earner]
49+
is_married: true
50+
households:
51+
household:
52+
members: [high_earner, low_earner]
53+
output:
54+
# Low earner's unused PA (12,570 − 10,000 = 2,570) is capped at PA × ma_rate
55+
# = 12,570 × 0.1 = 1,257, then rounded up to the nearest £10 → 1,260.
56+
# Low earner's partner has no unused PA to transfer, so low_earner's MA is 0.
57+
marriage_allowance: [1260, 0]
58+
59+
- name: Marriage-neutral IT — single person's adjusted net income is unchanged by the reform
60+
period: 2024
61+
absolute_error_margin: 1
62+
reforms:
63+
- policyengine_uk.reforms.cps.marriage_tax_reforms.marriage_neutral_it_reform
64+
input:
65+
people:
66+
adult:
67+
age: 35
68+
employment_income: 50_000
69+
benunits:
70+
benunit:
71+
members: [adult]
72+
is_married: false
73+
households:
74+
household:
75+
members: [adult]
76+
output:
77+
# has_spouse is False → split_income == original income → reform returns the
78+
# unadjusted net income (taxable_employment_income ≈ employment_income here).
79+
adjusted_net_income: 50_000
80+
81+
- name: Marriage-neutral IT — married couple splits unequal income
82+
period: 2024
83+
absolute_error_margin: 1
84+
reforms:
85+
- policyengine_uk.reforms.cps.marriage_tax_reforms.marriage_neutral_it_reform
86+
input:
87+
people:
88+
high_earner:
89+
age: 35
90+
employment_income: 60_000
91+
low_earner:
92+
age: 33
93+
employment_income: 10_000
94+
benunits:
95+
benunit:
96+
members: [high_earner, low_earner]
97+
is_married: true
98+
households:
99+
household:
100+
members: [high_earner, low_earner]
101+
output:
102+
adjusted_net_income: [35_000, 35_000]

0 commit comments

Comments
 (0)