Make tests fail when the behaviour they name breaks, #609
Labels
No labels
bug
duplicate
enhancement
help wanted
high priority
invalid
L
low priority
M
medium priority
needs refinement
optional
question
S
security
technical improvement
UX Improvement
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
local-it/mitgliederverwaltung#609
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Motivation
#605 removed dead code and hardened every test in the files it touched. Four review rounds kept finding the same two classes of defect in pre-existing tests: assertions that cannot fail, and tests that a stricter sibling already covers. A sweep after #605 found about 100 candidates of the same classes in the test files #605 did not touch (about 45 verified in context). They give false confidence, and like the #598 flakes they can turn into flaky tests.
The rules that would have prevented them are not in
CODE_GUIDELINES.mdyet. §4.3.4 only says "prefer selector-based assertions", and the examples in §4.3.4 and §4.3.5 show the anti-pattern themselves (assert html =~ "Members", which the sidebar always renders;assert html =~ "input"). So this issue starts with the guideline update and then applies it across the suite.Part 1 — CODE_GUIDELINES §4 (Testing Standards)
Add to §4.3.4 / §4.7 (wording to be finalized in the PR):
render(view) =~ textalso matches the layout: sidebar labels ("Members", "Groups", "Users", "Administration"),#flash-group/#client-error, the locale form, attributes such asrequiredorphx-change="validate", digits in ids and CSS classes, and the random session/CSRF tokens (a token once contained "BOM"). Scope every check to the element:has_element?(view, "#row-#{id} td", text),data-testid, stable ids, ARIA attributes.assert_flash/3andassert_field_error/3(test/support/live_view_assertions.ex); they pin the message to its element, and the coupling to the markup lives in one place.gettext/dgettext(with its bindings) or the exact value.a or bwhere one operand implies the other, or both mean the same, accepts wrong results.!= nil,is_list,Map.has_key?,{:ok, _}/{:error, _}, orlengthover a list the test built with a fixed size do not check behaviour. Match the specific struct/value; after an authorization call match the specific error (ForbiddenvsNotFound). A read that checks a delete needs an actor that may read the record, otherwise the read is denied either way.result != []oris_liststays green when a policy filters everything away.render_changeparams come from the input'sname. For a search input namedquery, send%{query: "x"};%{value: "x"}leaves the query empty and the "search" never runs.badge-*,btn-*,text-error); they change with the design system.Part 2 — apply it to the untouched test files
Grep-based sweep after #605 (counts are candidates, each area starts with a full read of its files and a break-proof per changed test):
show_membership_fees_test,form_membership_fee_type_test,show_groups_display_test,index_groups_integration_test,index_custom_fields_edge_cases_test,membership_fee_integration_test): about 24 — e.g.gettext("Groups")inshow_groups_display_test.exs:52/:69/:82/:169/:193/:218matches the sidebar;"Membership Fees" || …inshow_membership_fees_test.exs:59/:256is the tab label on every show page;"2024" || "date"inform_membership_fee_type_test.exs:213;html =~ "A" or …inindex_custom_fields_edge_cases_test.exs:88.user_policies_test.exs:178,member_policies_test.exs:335);{:error, _}without a reason incustom_field_deletion_test(6×),custom_field_value_policies_test(3×),custom_field_live/deletion_test,join_request_approval; tautologiestrue or :unknown or falseinhas_permission_test.exs:227/:246;is_list or is_map/not is_nilinhas_permission_integration_test.exs:42/:55/:70;role_policies_test.exs:27/:48/:69/:90never check the fixture role's id.membership_fee_settings_live_test,membership_fee_type_live/form_test): about 15 — e.g."3" || "Members" || "Mitglieder"(membership_fee_settings_live_test.exs:67,membership_fee_type_live/form_test.exs:125),|| html =~ "required"matches the input attribute (form_test.exs:194).user_liveindex/show/form/form_member_dropdown,role_live/show_test,auth_controller_test): about 12 — e.g. sidebar label "Users"/"Benutzer*innen" inuser_live/index_test.exs:33/:35; page-title ORs with an always-present operand (role_live/show_test.exs:283,user_live/show_test.exs:135); redirect-only creates (user_live/form_test.exs:43/:53, subsumed by:69/:91;form_member_dropdown_test.exs:74).global_settings_live_test,setting_env_test,join_live_test,join_live_email_failure_test,join_confirm_controller_test): about 9 — e.g.club_name != nilon anallow_nil? falseattribute (setting_env_test.exs:16).cycle_generator_edge_cases_test,foreign_key_test,membership_fee_type_test,custom_field_validation_test,custom_field_slug_test,member_groups_relationship_test): about 14 — e.g. leap-year February cycles only check!= niland nevercycle_end(cycle_generator_edge_cases_test.exs:462/:490/:517);result == :ok || match?({:error, _}, result)accepts everything (member_groups_relationship_test.exs:215).assert_field_error/3matches a substring, so a message that only gains a suffix stays green (tighten to the exact message);group_live/show_test(5×redirect || live_redirectwithout the flash),show_add_remove_members_test.exs:46, duplicate scenarios inshow_remove_member_test.exs("member is removed …" / "… successfully removed (verified in list)"),sort_header_component_test("shows ascending icon …" is contained in "multiple fields can have different icon states"), badge-variant asserts inmember_live/index_testbulk-trigger tests andstatus_color/1inmembership_fee_helpers_test(theme classes, coordinate with the button/theme work in #602).Not yet verified (read first):
membership_fee_type_live/form_test.exs:69/:89,role_live/show_test.exs:266,auth_controller_test.exs:40/:468,join_live_test.exs:134,join_live_email_failure_test.exs:49,join_confirm_controller_test.exs:74,mailer_test.exs:28,foreign_key_test.exs:124/:176,membership_fee_type_test.exs:168/:204,header_mapper_test.exs:128-129,smtp/config_test.exs:37.Acceptance criteria
CODE_GUIDELINES.md§4 contains the rules of Part 1, and its LiveView/component examples follow them.just ci-devandjust test --only uiare green.Notes