Skip to content

Instantly share code, notes, and snippets.

@devinschumacher
Last active November 19, 2024 14:25
Show Gist options
  • Select an option

  • Save devinschumacher/c3b89c50488a845feafdc96fc2e67fa8 to your computer and use it in GitHub Desktop.

Select an option

Save devinschumacher/c3b89c50488a845feafdc96fc2e67fa8 to your computer and use it in GitHub Desktop.
Software Testing Notes - vue/nuxt

Test Suite Levels

6. E2E/UI system tests
5. E2E/UI isolated tests
-
4. API tests (out of process)
3. Integration tests (in memory)
-
2. Component tests (in memory)
1. Unit tests (in memory)
  1. Unit Tests
  2. Component Tests
  3. Integration tests: look almost exactly like regular unit tests, but some of the dependencies are not stubbed out.
  4. API tests: We are no longer instantiating the test in memory. The first time we need to actually deploy the application under test (or at least part of it) and invoke it through the network to run the test properly.

Unit Tests Overview

Unit Test: a piece of code that invokes a unit of work & checks one specific exit point as an end result of that work.

Unit test -> "unit" = "unit of work"

Unit of Work:

  • has entry point(s) (aka a 'beginning')
  • has exit point(s) (aka an 'ending')
  • does something useful

Best Practice: Write one unit test for each exit point

Entry points: Where the test triggers the unit of work

Exit points: There are 3 types of exit points, they either:

  1. Return a value (or error) -- "return based exit points"
  2. Noticeable state change -- "state based exit points"
  3. Calls a 3rd party dependency

Testing "return based exit points" flow:

  1. Trigger an entry point
  2. Get something back
  3. Check the value you got back

Testing "state based exit points" flow:

  1. Call something
  2. Do another call to check something else
  3. Check if everything went as expected

Unit Test Checklist

You must be able to answer 'yes' to all of the following questions.

  • Can i run & get results form a test i wrote two weeks/months/years ago?
  • Can any member of my team run & get results from tests I wrote two weeks/months/years ago?
  • Can i run all the tests i've written in no more than a few mins?
  • Can i run all the tests i've written at the push of a button?
  • Can i write a basic test in no more than a few minutes?
  • Do my tests pass when there are bugs in another team's code?
  • Do my tests show the same results when run on different machines/environments?
  • Do my tests stop woriing it there's no database, network or deployment?
  • If i delete, move or change one test - do other tests remain unaffected?

Unit Test Best Practices

  1. Use the "USE" naming system when naming tests. "USE" naming:
  • U: Unit under test
  • S: Scenario
  • E: Expectation

Example implementation: test('verifyPassword, given a failing rule, returns errors')

  1. U: verifyPassword

  2. S: given a failing rule

  3. E: returns errors

  4. Avoid writing asserts and the method call in the same action

# BAD
expect(verifier.verify("any value")[0]).toContain("fake reason");`

# GOOD:
const result = verifier.verify("any value");
expect(result[0]).toContain("fake reason");
  1. Dont leave magic values in your tests

  2. Try not to use "setups" at all (such as beforeEach methods). Introduce helper methods to simplify the tests' arrange part, and use those helper methods in each test.

SOP: Writing Unit Tests for Components

  1. establish the acceptance criteria - write down the things the user story should accomplish
  2. put those items into describe()/it() blocks with no code
  3. fill in the code for the items
  4. ....?

components/

Verify the static and dynamic aspects of the component, ensuring it behaves as expected in isolation before integrating it into larger parts of the application. Confirm both presentation & behavior.

A Component deals with several concerns, among others:

  • It renders the template into the HTML DOM.
  • It accepts data from parent Components using Input properties.
  • It emits data to parent Components using Outputs.
  • It reacts to user input by registering event handlers.
  • It renders the content (ng-content) and templates (ng-template) that are passed.
  • It binds data to form controls and allows the user to edit the data.
  • It talks to Services or other state managers.
  • It uses routing information like the current URL and URL parameters.

All these tasks 👆 need to be tested properly.

Types of component unit tests:

  • Rendering tests: Does the component render correctly with the default props or state?
  • User Interaction: Do buttons, dropdowns, and other interactive elements behave as expected?
  • Dynamic Behavior: Does the component update/react correctly when props, slots, or inputs change?
  • State Management: If the component uses local state, is it consistent with user interactions?

Best practices

  1. Focus on User Actions and Outcomes: Test how the component responds to user interactions (clicks, selections, input), rather than how it’s implemented internally.
  2. Verify Dynamic Content: If content in an element is dynamic or conditional, test that it displays the correct information to the user.
  3. Prioritize Accessibility Elements: Validate that accessible elements, like labels for form fields, are correctly associated with their corresponding inputs.
  4. Test Interactive Elements: If users can interact with an element (e.g., click a button, open a dropdown, enter text), include it in your tests.
  5. Skip Purely Visual or Static Elements: Avoid testing static text, simple structural elements, or purely decorative items (e.g., <div> for layout) unless they impact user functionality or accessibility directly.
  6. Test Critical Conditional Rendered Elements: If an element is shown or hidden based on certain conditions (e.g., error messages, loading spinners), ensure it appears as expected when the conditions are met.

pages/

  • Rendering: Does the page render correctly?
  • Data Loading: Does the page correctly fetch and display server-provided data for the user?
  • Error Handling: Are errors (e.g., 404, API failures) displayed in a user-friendly way?
  • Navigation: Are navigation links and redirections working correctly?

stores/

  • State Changes: Are user actions (e.g., toggling dark mode, adding items to a cart) accurately reflected in the store state?
  • Persistence: Is state persisting correctly (e.g., localStorage, cookies) when required?
  • Derived State: Are computed properties or getters providing correct values based on the store's state?
  • Reset and Initialization: Does the store initialize/reset correctly during user workflows?
  • Concurrency: Are multiple users or sessions handled gracefully, avoiding race conditions?

composables/

  • Functionality: Does the composable return the expected state or functions when used?
  • Reactiveness: Are changes to inputs (e.g., user search query, filters) properly tracked and reactive?
  • Error Handling: Are edge cases handled (e.g., empty results, invalid input)?

server/api

Focus: Validate user-facing API endpoints for reliability and correctness.

  • Endpoint Response: Does the endpoint return the correct data format (e.g., JSON) for different user queries?
  • Authentication: Are APIs correctly handling authenticated and unauthenticated users?
  • Error Handling: Are appropriate status codes and messages returned for errors (e.g., 404, 500)?
  • Security: Are user inputs sanitized to prevent attacks like SQL injection or XSS?

utils/

  • Correctness: Do utilities produce accurate results for all expected inputs?
  • Edge Cases: Are edge cases handled gracefully (e.g., empty strings, null values)?
  • Performance: Do utilities perform well when handling large datasets or complex calculations?
  • Consistency: Are results predictable and consistent for the same input?
  • Integration: Do utilities work seamlessly when used across components, pages, or stores?

HTML Elements and Whether to Test Them

1. Interactive Elements

  • Button (<button>) - Yes
  • Test clicks and check if they trigger the correct actions or events.
  • Link (<a>) - Yes
  • Test that it navigates to the expected URL or triggers an event (e.g., in SPAs).
  • Input (<input>) - Yes
  • Test value updates, focus, placeholder text, and any expected validation.
  • Select (<select>) - Yes
  • Test that it displays options correctly and updates value on selection.
  • Textarea (<textarea>) - Yes
  • Test user input handling and any validation (like max length).
  • Checkbox (<input type="checkbox">) - Yes
  • Test if it toggles state correctly and emits change events.
  • Radio Button (<input type="radio">) - Yes
  • Test selection behavior and group handling (only one should be selectable within a group).

2. Text and Display Elements

  • Heading (<h1>, <h2>, etc.) - No
  • Generally, don’t test headings unless they are dynamically generated and critical to user understanding.
  • Paragraph (<p>) - No
  • Testing static text is usually unnecessary unless it’s dynamically generated.
  • Span (<span>) - No
  • Generally skip, unless it contains dynamic content that needs to be validated.
  • Label (<label>) - Yes
  • Test labels to confirm they associate correctly with inputs and provide accessible information.

3. Media Elements

  • Image (<img>) - Yes
  • Test that the correct image source (src) is displayed, especially if it’s dynamic.
  • Video (<video>) - No
  • Only test if video controls or autoplay are required to function in a certain way.
  • Audio (<audio>) - No
  • Similar to video, generally skip unless there are specific controls users interact with.

4. Container and Layout Elements

  • Div (<div>) - No
  • Skip unless it contains dynamic content or functionality (e.g., modals or collapsible sections).
  • Section (<section>) - No
  • Generally skip, as it’s usually structural, unless there is specific dynamic content within.
  • Article (<article>) - No
  • Same as section; skip unless there is dynamically generated or important user-facing content.
  • Nav (<nav>) - No
  • Skip unless navigation links within should be tested for accessibility or functionality.

5. List Elements

  • Ordered List (<ol>) - No
  • Only test if list items are dynamically generated and their order matters.
  • Unordered List (<ul>) - No
  • Similar to <ol>, skip unless dynamic content is crucial to user experience.
  • List Item (<li>) - Yes
  • Test if items are dynamic or based on user interaction (e.g., a to-do list).

6. Table Elements

  • Table (<table>) - Yes
  • Test tables if they display dynamic data, especially for row and column content.
  • Table Row (<tr>) - No
  • Generally skip individual rows unless they display critical data.
  • Table Cell (<td>) - No
  • Skip testing individual cells unless the content is dynamic or interactive.
  • Table Header (<th>) - No
  • Skip unless headers are dynamic or display important sorting/filtering options.

7. Form Elements

  • Form (<form>) - Yes
  • Test the form submission process, including validation and handling of form data.
  • Fieldset (<fieldset>) - No
  • Usually unnecessary to test unless it contains conditional or dynamic form fields.
  • Legend (<legend>) - No
  • Skip unless it’s dynamic and critical to the form’s clarity.

8. Interactive Container Elements

  • Dialog (<dialog>) - Yes
  • Test visibility, opening, and closing actions, especially if it’s a modal.
  • Details/Summary (<details>, <summary>) - Yes
  • Test that it expands/collapses correctly if used interactively by the user.

9. Interactive State Elements

  • Progress (<progress>) - Yes
  • Test the value attribute, ensuring it reflects progress as expected.
  • Meter (<meter>) - No
  • Skip unless user needs to interact or see specific dynamic values.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment