{"id":20468978,"url":"https://github.com/neko-box-coder/sstest","last_synced_at":"2025-07-23T23:07:41.314Z","repository":{"id":117867591,"uuid":"594130404","full_name":"Neko-Box-Coder/ssTest","owner":"Neko-Box-Coder","description":"🧪 A simple fancy test framework","archived":false,"fork":false,"pushed_at":"2025-07-06T03:59:18.000Z","size":341,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-06T04:34:21.047Z","etag":null,"topics":["cpp","cpp11","test","test-framework","testing","testing-framework"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Neko-Box-Coder.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-01-27T17:09:47.000Z","updated_at":"2025-06-06T16:41:36.000Z","dependencies_parsed_at":null,"dependency_job_id":"e6c9a403-77ab-4641-9870-b80e6ef97c24","html_url":"https://github.com/Neko-Box-Coder/ssTest","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Neko-Box-Coder/ssTest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neko-Box-Coder%2FssTest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neko-Box-Coder%2FssTest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neko-Box-Coder%2FssTest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neko-Box-Coder%2FssTest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Neko-Box-Coder","download_url":"https://codeload.github.com/Neko-Box-Coder/ssTest/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neko-Box-Coder%2FssTest/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266765209,"owners_count":23980726,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","cpp11","test","test-framework","testing","testing-framework"],"created_at":"2024-11-15T14:07:31.162Z","updated_at":"2025-07-23T23:07:41.277Z","avatar_url":"https://github.com/Neko-Box-Coder.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🧪 ssTest\n\nAn easy to use, flexible, single header, C++11 testing framework.\n\nA testing framework is literally just a fancy way of printing asserts results. \nThe last thing you want is to spend so much time trying to figure out how to import and use a testing framework.\n\n## 📦️ Installation\n\nJust include `ssTest.hpp` into your project\nTo disable color output, either `#ssTEST_NO_COLOR_OUTPUT 1` before the include or set it in cmake option\n\n## 🏃 Quick Start\nHere is a quick example of what ssTest looks like in a simple example. \nMany other features are covered in 📖 [Documentations](#documentations)\n\n```cpp\nint AddOne(int input) { return input + 1; }\n\nint main()\n{\n    int testVar = 0;\n    \n    //Name of test group is optional\n    ssTEST_INIT_TEST_GROUP(\"ssTest Quick Start\");\n    \n    ssTEST_COMMON_SETUP\n    {\n        testVar = 1;\n    };\n    \n    ssTEST(\"testVar Should Be Initialized By Common Setup\")\n    {\n        ssTEST_OUTPUT_ASSERT(testVar == 1);\n    };\n    \n    ssTEST(\"Setup And Execution Example\")\n    {\n        //ssTEST_OUTPUT_SETUP is optional\n        ssTEST_OUTPUT_SETUP\n        (\n            int testVar2 = 10;\n        );\n        \n        //ssTEST_OUTPUT_EXECUTION is optional\n        ssTEST_OUTPUT_EXECUTION\n        (\n            testVar = AddOne(testVar2);\n        );\n        \n        ssTEST_OUTPUT_ASSERT(testVar == testVar2 + 1);\n    };\n\n    ssTEST_END_TEST_GROUP();\n}\n\n```\n\n## 📖 Documentations\n\n### 😶‍🌫️ Concepts\nIn ssTest, **asserts** (`ssTEST_OUTPUT_ASSERT()`) are grouped into **test** (`ssTEST()`), \n**tests** are grouped into **test group** (`ssTEST_INIT_TEST_GROUP()`).\n\nYou can also name each assert, test and test group however you want.\n\nTest groups are also nestable so you can have a hierarchy of tests.\n\n\u003cdetails\u003e \n\n\u003csummary\u003eSo you can have a simple structure like this (Expandable)\u003c/summary\u003e\n\n```text\n- Test Group: My App\n    - Test: Feature A Scenario 1\n        - Assert: Action 1\n        - Assert: Action 2\n    - Test: Feature A Scenario 2\n        - Assert: Action 1\n        - Assert: Action 2\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e \n\n\u003csummary\u003eOr a more complex structure like this (Expandable)\u003c/summary\u003e\n\n```text\n- Test Group: My App\n    - Test: Feature A\n        - Test Group: Feature A\n            - Test: Feature A Scenario 1\n                - Assert: Action 1\n                - Assert: Action 2\n            - Test: Feature A Scenario 2\n                - Assert: Action 1\n                - Assert: Action 2\n    \n    - Test: Feature B\n        - Test Group: Feature B\n            - Test: Feature B Scenario 1\n                - Assert: Action 1\n                - Assert: Action 2\n            - Test: Feature B Scenario 2\n                - Assert: Action 1\n                - Assert: Action 2\n```\n\n\u003c/details\u003e\n\nEach test in a test group can optionally share a **common setup** (`ssTEST_COMMON_SETUP()`) and \na **common cleanup** function (`ssTEST_COMMON_CLEANUP()`).\n\n### 📂 Test Group\n\nEach test group must be inside a **function that returns an `int`**.\n\nIf that test group is successful, it will **return 0**. Otherwise, it will return **non 0**.\n\nIt is common to put a single test group in a single `.cpp` file inside the `int main()` function\nbut you can do any other way you see fit.\n\n#### Initializing A Test Group (Required)\n- `ssTEST_INIT_TEST_GROUP();` : Initialize a test group with the name of the current file\n- `ssTEST_INIT_TEST_GROUP(\"testGroupName\");`: Initialize a test group with `testGroupName`.\n\n#### Common Setup And Cleanup\n\u003e These should be called before declaring the first test (`ssTEST()`) in the test group\n\n- `ssTEST_COMMON_SETUP(){...};`: A common setup function that is called before each test in the test group\n- `ssTEST_COMMON_CLEANUP(){...};`: A common cleanup function that is called after each test in the test group\n- `ssTEST_DISABLE_COMMON_SETUP_CLEANUP_BETWEEN_TESTS();`: Disable calling the common setup and cleanup functions **between** tests. \nBut still be called at the beginning and end of the test group.\n\n#### Ending A Test Group (Required)\n- `ssTEST_END_TEST_GROUP();`: Finishes the declarations of all the tests, executes them and outputs the results.\n\nA short example of a test group\n\n```c++\nint main()\n{\n    int testVar = 0;\n    int cleanUpCount = 0;\n    \n    ssTEST_INIT_TEST_GROUP();\n    \n    ssTEST_COMMON_SETUP{ testVar = 1;};\n    ssTEST_COMMON_CLEANUP{ cleanUpCount++; };\n    //ssTEST_DISABLE_COMMON_SETUP_CLEANUP_BETWEEN_TESTS();\n    \n    ssTEST(\"A Normal Test\")\n    {\n        ssTEST_OUTPUT_ASSERT(testVar == 1);\n        testVar = 5;\n    };\n    \n    ssTEST(\"Another Normal Test\")\n    {\n        ssTEST_OUTPUT_ASSERT(testVar == 1);\n        ssTEST_OUTPUT_ASSERT(cleanUpCount == 1);\n        \n        //If ssTEST_DISABLE_COMMON_SETUP_CLEANUP_BETWEEN_TESTS() is called, \n        //  testVar would be 5 and cleanUpCount would be 0\n    };\n\n    ssTEST_END_TEST_GROUP();\n}\n```\n\n#### Nesting Test Groups (Advanced)\n\n\u003cdetails\u003e\n\nJust like a normal test group, you just need to call `ssTEST_INIT_TEST_GROUP()` \ninside a function that is called from the outer test group.\n\nHowever, the (optional) extra step you need to do to make it format well is to pass the indentation\nstring from the outer test group to the inner test group.\n\n\u003e These should be called before declaring the first test (`ssTEST()`) in the test group\n- `ssTEST_GET_NESTED_TEST_GROUP_INDENT();`: Gets the indentation string for nested test group\n- `ssTEST_SET_TEST_GROUP_INDENT(\"indent\");`: Sets the indentation level using the string `indent`\n\nAx example would be\n\n```c++\nint AssertGroupA(std::string outerIndent)\n{\n    ssTEST_INIT_TEST_GROUP(\"Test Group A\");\n    ssTEST_SET_TEST_GROUP_INDENT(outerIndent);\n    \n    ssTEST(\"A Normal Test In Group A\")\n    {\n        ssTEST_OUTPUT_ASSERT(true);\n    };\n\n    ssTEST_END_TEST_GROUP();\n}\n\nint main()\n{\n    ssTEST_INIT_TEST_GROUP(\"Root Test Group\");\n    ssTEST(\"A Normal Test\")\n    {\n        ssTEST_OUTPUT_ASSERT(true);\n    };\n\n    ssTEST(\"Calling A Nested Test Group\")\n    {\n        ssTEST_OUTPUT_ASSERT( AssertGroupA( ssTEST_GET_NESTED_TEST_GROUP_INDENT() ) == 0 );\n    };\n\n    ssTEST_END_TEST_GROUP();\n}\n```\n\n\u003c/details\u003e\n\n#### Disable Output For Certain Actions Inside A Test (Advanced)\n\u003cdetails\u003e\n\n\u003e These should be called before declaring the first test (`ssTEST()`) in the test group\n- `ssTEST_DISABLE_OUTPUT_SETUP();`: Disable outputting to the console for the test setup action\n- `ssTEST_DISABLE_OUTPUT_EXECUTION();`: Disable outputting to the console for the test execution action\n- `ssTEST_DISABLE_OUTPUT_ASSERT();`: Disable outputting to the console for the test assert action.\nIf the assert fails however, it will still output associated line that failed the assert.\n\n\u003c/details\u003e\n\n### 🧪 Test\n\n- `ssTEST(\"testName\"){statement_1; statement_2; ...};`: Creates a test with `testName`\n- `ssTEST_ONLY_THIS(\"testName\"){statement_1; statement_2; ...};`: Creates a test with `testName` and only runs this test, \ngreat when debugging a single test.\n- `ssTEST_SKIP(\"testName\"){statement_1; statement_2; ...};`: Creates a test with `testName` and skips it\n\nExample:\n\n```c++\nint main()\n{\n    ssTEST_INIT_TEST_GROUP();\n    \n    ssTEST(\"A Normal Test\")\n    {\n        ssTEST_OUTPUT_ASSERT(true);\n    };\n    \n    ssTEST_ONLY_THIS(\"Will Only Run This Test\")\n    {\n        ssTEST_OUTPUT_ASSERT(true);\n    };\n    \n    ssTEST_SKIP(\"Will Skip This Test\")\n    {\n        ssTEST_OUTPUT_ASSERT(true);\n    };\n    \n    ssTEST_END_TEST_GROUP();\n}\n```\n\n### 👊 Actions And Assertions Inside A Test\n\n#### Required Assertions\n- `ssTEST_OUTPUT_ASSERT(assertionStatement);`: Assert and output the assertion statement.\n- `ssTEST_OUTPUT_ASSERT(\"info\", assertionStatement);`: Assert and output the assertion statement with info.\n- `ssTEST_OUTPUT_ASSERT(\"info\", assertValue, expectedValue);`: Assert if the `assertValue` and `expectedValue` are \nequal and output result with `info`.\n- `ssTEST_OUTPUT_ASSERT(\"info\", assertValue, expectedValue, operator);`: Assert if the `assertValue` \nand `expectedValue` returns true when compared with the `operator` and output result with `info`.\n\n#### Optional Assertions\n- `ssTEST_OUTPUT_OPTIONAL_ASSERT(...);`: Execute the assertions with arguments same as \"Required Assertions\"\nbut failure of the assertion will **not** fail the test.\n\n#### Test Setup and Execution\n- `ssTEST_OUTPUT_SETUP(statement_1; statement_2; ...);`: Executes and output the setup statements.\n- `ssTEST_OUTPUT_EXECUTION(statement_1; statement_2; ...);`: Executes and output the execution statements.\n\n#### Skipping Actions\n- `ssTEST_OUTPUT_SKIP_ASSERT(...)`: **Skips** the assertion with arguments same as \"Required Assertions\".\n- `ssTEST_OUTPUT_SKIP_SETUP(statement_1; statement_2; ...);`: **Skips** the test setup action.\n- `ssTEST_OUTPUT_SKIP_EXECUTION(statement_1; statement_2; ...);`: **Skips** the test execution.\n\n#### Outputting Values (Up to 5 values) When An Assertion Failed\n- `ssTEST_OUTPUT_VALUES_WHEN_FAILED(comma, separated, values,...);`: Output the values of the variables when the assertion failed.\n\n#### Outputting Values In Test Format\n- `ssTEST_OUTPUT(expression);`: Output message which expands to `std::cout \u003c\u003c expression \u003c\u003c std::endl`\n\n#### Calling Common Setup And Cleanup Manually (Advanced)\n- `ssTEST_CALL_COMMON_SETUP();`: Calls the common setup function manually\n- `ssTEST_CALL_COMMON_CLEANUP();`: Calls the common cleanup function manually\n\n### 📝 Complete Example\n\nLet's say I have a C memory allocator:\n\n```c\n//An Example Class To Test\ntypedef struct\n{\n    int PageSize;\n    int PageCount;\n    short* AllocateID_Table;\n    int NextAllocateID;\n    char* Memory;\n} MyMemoryAllocator;\n\nMyMemoryAllocator MyMemoryAllocator_Create(size_t pageSize, size_t pageCount);\nbool MyMemoryAllocator_Destroy(MyMemoryAllocator* allocator);\nvoid* MyMemoryAllocator_Allocate(MyMemoryAllocator* allocator, size_t size);\nbool MyMemoryAllocator_Free(MyMemoryAllocator* allocator, void* memory);\n\n```\n\nMy test file could look like this:\n\n```c++\nint main()\n{\n    MyMemoryAllocator testAllocator;\n    \n    ssTEST_INIT_TEST_GROUP();\n\n    ssTEST_COMMON_SETUP\n    {\n        testAllocator = MyMemoryAllocator_Create(64, 12);\n    };\n\n    ssTEST_COMMON_CLEANUP\n    {\n        MyMemoryAllocator_Destroy(\u0026testAllocator);\n    };\n\n    ssTEST(\"MyMemoryAllocator_Create Should Not Crash When Requesting Zero Memory\")\n    {\n        //We want to create our own memory allocator and request empty memory\n        ssTEST_CALL_COMMON_CLEANUP();\n        \n        ssTEST_OUTPUT_EXECUTION\n        (\n            testAllocator = MyMemoryAllocator_Create(64, 0);\n        );\n        \n        ssTEST_OUTPUT_ASSERT(testAllocator.PageSize == 64);\n        ssTEST_OUTPUT_ASSERT(testAllocator.PageCount == 0);\n    };\n    \n    ssTEST(\"MyMemoryAllocator_CreateShared Should Create Shared Memory Allocator\")\n    {\n        ssTEST_OUTPUT_SETUP\n        (\n            const int pageSize = 64;\n            const int pageCount = 4;\n            MyMemoryAllocator sharedAllocator;\n            void* sharedMemory = MyMemoryAllocator_Allocate(\u0026testAllocator, \n                                                            pageSize * pageCount + \n                                                            pageCount * sizeof(short));\n        );\n        \n        ssTEST_OUTPUT_EXECUTION\n        (\n            sharedAllocator = MyMemoryAllocator_CreateShared(sharedMemory, pageSize, pageCount);\n        );\n        \n        ssTEST_OUTPUT_ASSERT(sharedAllocator.AllocateID_Table == (short*)sharedMemory);\n        ssTEST_OUTPUT_ASSERT(sharedAllocator.Memory == (char*)sharedMemory + pageCount * sizeof(short));\n        ssTEST_OUTPUT_ASSERT(sharedAllocator.PageSize == pageSize);\n        ssTEST_OUTPUT_ASSERT(sharedAllocator.PageCount == pageCount);\n        \n        MyMemoryAllocator_Destroy(\u0026sharedAllocator);\n    };\n\n    ssTEST(\"MyMemoryAllocator_Allocate Should Allocate Memory When Enough Memory Is Available\")\n    {\n        ssTEST_OUTPUT_SETUP\n        (\n            const int pageSize = 64;\n            char* memory;\n            int pageCount = 4;\n        );\n        \n        for(int i = 0; i \u003c 2; i++)\n        {\n            ssTEST_OUTPUT_EXECUTION\n            (\n                memory = (char*)MyMemoryAllocator_Allocate(\u0026testAllocator, pageSize * pageCount);\n            );\n        }\n        \n        ssTEST_OUTPUT_EXECUTION\n        (\n            *memory = 17;\n            *(memory + pageSize * pageCount - 1) = 17;\n        );\n        \n        //This also works without a message as well\n        ssTEST_OUTPUT_ASSERT(\"Memory Result\", memory != NULL);\n        \n        //Asserting by passing two values (to compare equality) will also print the values\n        ssTEST_OUTPUT_ASSERT(\"Reading to beginning of memory\", *memory == 17);\n        \n        //You can also specify the operator to use for comparison\n        ssTEST_OUTPUT_ASSERT(   \"Reading to end of memory\", \n                                *(memory + pageSize * pageCount - 1), 17, ==);\n    };\n    \n    ssTEST(\"MyMemoryAllocator_Allocate Should Return NULL When Not Enough Memory Is Available\")\n    {\n        ssTEST_OUTPUT_SETUP\n        (\n            const int pageSize = 64;\n        );\n        \n        //Let's say this is not implemented or we know it is crashing, we can skip the assert \n        //or even skip the whole test with ssTEST_SKIP\n        ssTEST_OUTPUT_SKIP_ASSERT(  \"Allocation Result\", \n                                    (char*)MyMemoryAllocator_Allocate(  \u0026testAllocator, \n                                                                        pageSize * 250) == NULL);\n    };\n    \n    ssTEST(\"MyMemoryAllocator_Allocate Should Return NULL When Zero Size Is Passed In\")\n    {\n        ssTEST_OUTPUT_SETUP\n        (\n            char* memory;\n        );\n        (void)memory;\n        \n        ssTEST_OUTPUT_EXECUTION\n        (\n            memory = (char*)MyMemoryAllocator_Allocate(\u0026testAllocator, 0);\n        );\n        \n        //Let's say there's a bug in the allocator which will probably fail the assert, \n        //but we still want to know the result. An optional assert can be used in this case.\n        ssTEST_OUTPUT_OPTIONAL_ASSERT(\"Allocation Result\", memory == NULL);\n    };\n\n    //etc...\n\n    ssTEST_END_TEST_GROUP();\n}\n```\n\nFor full example, see `Src/ssTestExample.cpp`\n\n### 🎯 Command Line Arguments\n\nssTest can be controlled through command line arguments. To enable this, pass the program arguments to `ssTEST_PARSE_ARGS`:\n\n```cpp\nint main(int argc, char* argv[])\n{\n    ssTEST_INIT_TEST_GROUP();\n    ssTEST_PARSE_ARGS(argc, argv);\n    \n    ssTEST(\"My Test\")\n    {\n        //...\n    };\n    \n    ssTEST_END_TEST_GROUP();\n}\n```\n\nAvailable options:\n\n```\n--help                   Show help message\n--no-setup-output        Disable setup output\n--no-execution-output    Disable execution output\n--no-assert-output       Disable assert output\n--test-only \u003cname\u003e       Only run the specified test\n--skip-test \u003cname\u003e       Skip the specified test\n--list-only              Only list available tests\n--min-output             Only show the minimum output\n--assert-output          Only show assertion output\n```\n\nNote: `ssTEST_PARSE_ARGS` must be called after `ssTEST_INIT_TEST_GROUP` but before defining any tests.\n\nHere are some screenshots of the output of the tests\n\n![](./ListOfTests.png)\n![](./SimpleTest.png)\n![](./ComplexTest.png)\n![](./SkipAsserts.png)\n![](./OptionalAsserts.png)\n\n\n### 📊 Tests And Assert Results\n\nWhen all the tests have finished running, you can quickly find the ones that failed by searching\nfor certain keywords from different types of result output.\n\n#### Assertions\n- Required Assertions:\n    - `Assertion Passed (O)` when passed\n    - `Assertion Failed (X)` when failed\n    - `Error Caught (X) ...` when an error is caught\n- Optional Assertions:\n    - `Assertion Passed (+)` when passed\n    - `Assertion Failed (-)` when failed\n    - `Error Caught (-) ...` when an error is caught\n- Skipped Assertions:\n    - `Assertion Skipped (/)` when skipped\n\n#### Test\n- `...passed all tests (OOO)` when all assertions passed\n- `...failed some tests (XXX)` when some assertions failed\n- `...Error Caught (XXX)...` when an error is caught\n\nNotice each result has a symbol inside the parentheses, which makes it easier to identify the result.\n\nYou can easily capture the symbol inside the parentheses to quickly output the related failures in CI.\nFor example, if you want the context of all failed asserts, you can quickly do\n```bash\ncat testResults.txt | grep -B15 \"(X)\"\n```\n\nOr in powershell\n```powershell\nSelect-String -path .\\testResult.txt -Pattern \"\\(X\\)\" -Context 15,0\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneko-box-coder%2Fsstest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fneko-box-coder%2Fsstest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneko-box-coder%2Fsstest/lists"}