{"id":23331204,"url":"https://github.com/sassoftware/jsl-hamcrest","last_synced_at":"2026-01-20T04:32:08.558Z","repository":{"id":35509369,"uuid":"211145954","full_name":"sassoftware/jsl-hamcrest","owner":"sassoftware","description":"Implementation of Hamcrest for JSL. ","archived":false,"fork":false,"pushed_at":"2023-02-06T19:27:57.000Z","size":1763,"stargazers_count":11,"open_issues_count":12,"forks_count":8,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-07T10:48:19.241Z","etag":null,"topics":["hamcrest","jmp","jsl"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sassoftware.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","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}},"created_at":"2019-09-26T17:33:21.000Z","updated_at":"2024-02-10T20:36:17.000Z","dependencies_parsed_at":"2024-12-20T22:32:37.007Z","dependency_job_id":"2b3c6cbf-7b50-4415-9a9c-67bf0e7f9732","html_url":"https://github.com/sassoftware/jsl-hamcrest","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/sassoftware/jsl-hamcrest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Fjsl-hamcrest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Fjsl-hamcrest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Fjsl-hamcrest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Fjsl-hamcrest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sassoftware","download_url":"https://codeload.github.com/sassoftware/jsl-hamcrest/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sassoftware%2Fjsl-hamcrest/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28596079,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T02:08:49.799Z","status":"ssl_error","status_checked_at":"2026-01-20T02:08:44.148Z","response_time":117,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["hamcrest","jmp","jsl"],"created_at":"2024-12-20T22:32:27.118Z","updated_at":"2026-01-20T04:32:08.543Z","avatar_url":"https://github.com/sassoftware.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# JSL-Hamcrest\n\nImplementation of Hamcrest (hamcrest.org) for JSL. Write expressive tests with\ninformative failure messages. Make tests more self-contained and reduce\ninformation spilling between them.\n\n## Abstract - JMP Discovery Summit 2019\n\n### Automate the Testing of JSL Using Hamcrest\n\nHave you written some JSL and gotten tired of manually testing after every change?\nHave you inadvertently broken some piece of your application, or has the fear of\ndoing so prevented you from making the changes you want to make? With automated\ntesting, you can be more confident in your changes. Now available is a set of tools\nfor automating the testing of JSL. This framework includes the creation of tests\nand test cases, as well as an implementation of the well-known Hamcrest assertion\nlibrary. Hamcrest provides flexible ways to assert what you know to be true about\nthe behavior of your JSL. These tools are full-featured and can be specialized for\nyour needs. In fact, JMP development even uses them to test JMP itself. The\npresentation will cover this framework and its use in testing an example JSL\napplication, from individual functions to the automation of GUI interactions.\n\n## Documentation\n\nCheck out our [documentation](https://sassoftware.github.io/jsl-hamcrest/#File:Matchers-Index.txt) website for detailed information about using JSL-Hamcrest.\n\n## Getting Started\n\n### System Requirements\n\n* Works on Mac and Windows operating systems\n* Requires JMP 14.1 or higher\n\n### Installing\n\nOpen the `JSL-Hamcrest.jmpaddin` file in JMP to install.\n\n### Basic Usage\n\nJSL-Hamcrest has several different pieces that you can choose to use for testing. You can use most of these separately or you can combine them.\nThese include assertions (`ut assert that` and matchers), test cases (`ut test`, `ut test case`), mock functions (`ut mock function`), and the\nreporter (`ut global reporter`) that underlies all of this. The most common scenario is a combination of test cases and assertions like:\n\n```jsl\nformulas test case = ut test case(\"Formulas\")\n    \u003c\u003c Setup(Expr(\n        dt = Open(\"$SAMPLE_DATA\\Big Class.jmp\");\n    ))\n    \u003c\u003c Teardown(Expr(\n        Close(dt, NoSave); // Unnecessary\n    ))\n    \u003c\u003c Skip If(Expr(Day Of Week(Today()) == 4), \"It's Wednesday\");\n\nut test(formulas test case, \"Advance Row\", Expr(\n    dt \u003c\u003c new column(\"Test\", Formula(Row()));\n    ut assert that(Expr(dt:Test \u003c\u003c Get Values), (1::40)`);\n));\n\nut test(formulas test case, \"Char\", Expr(\n    dt \u003c\u003c new column(\"Test\", Formula(Char(Row())));\n    ut assert that(Expr(dt:Test \u003c\u003c Get Values), ut starts with({\"1\", \"2\", \"3\"}));\n));\n\nut test(\"Big Class\", \"Row Count\", Expr(\n\tdt = Open(\"$SAMPLE_DATA\\Big Class.jmp\");\n\tut assert that(Expr(N Rows(dt)), ut greater than(38), \"large\");\n));\n```\n\nThe `ut test case` allows a `Setup` and `Teardown` expression that are run before and after *each* `ut test`. The `ut test` also ensures that all windows, tables, errors, and symbols get cleaned up so you can focus on the test itself. The `ut assert that` is how you write an actual assertion. The first argument (the test expression) should always be wrapped in `Expr` so we can test errors and report better test failures. The second argument (the expected) can be a value or a `matcher` like `ut starts with`. These are declarative descriptions of what you would like to test which give informative descriptions and failures. You can use `ut assert that` outside of `ut test` if you want.\n\nMock functions can be used to test callbacks. These look like:\n\n```jsl\nData Table List Subscription Test = ut test case(\"Data Table List Subscription\")\n    \u003c\u003cSetup(Expr(\n        mock open = ut mock function({dt}, Expr(\"open\"; .));\n        sub name = Subscribe to Data Table List(, OnOpen( Function({dt}, mock open:fn(dt)) ) );\n    ))\n    \u003c\u003cTeardown(Expr(\n        Unsubscribe to Data Table List(sub name, \"ALL\");\n        ut verify mock(mock open);\n    ));\n\nut test(Data Table List Subscription Test, \"OnOpen/OnClose called for Open\", Expr(\n    ut expect call(mock open, { ut name( ut host table name( \"Big Class\" ) ) } );\n    Open( \"$SAMPLE_DATA/Big Class.jmp\" );\n));\n```\n\n`ut mock function` defines a instrumented function that you can use as a callback. `ut expect call` adds expectations about how many times that function will\nbe called and with what arguments. And `ut verify mock` makes sure the expectations are met.\n\nFinally, there are reporters. Most of the time, you don't need to worry about the reporter since it is handled automatically by the add-in when initialized\nor when a test runner GUI is added to a script editor. However, if you want to do something else, you need to know how to use a reporter. There can only\nbe *one* reporter, `ut global reporter`, and it determines what happens when test successes, failures, *etc* are reported from `ut mock function`,\n`ut assert that`, `ut test`, *etc*. Here are examples showing reporters and how to use them.\n\n```jsl\n// Does nothing\nut global reporter = ut reporter();\nut assert that(Expr(1 + 1), 3);\n```\n```jsl\n// Writes to the log as successes, failures, etc come in\nut global reporter = ut streaming log reporter with skip();\nut assert that(Expr(1 + 1), 3);\n```\n```jsl\n// Collects into a buffer until you show the report explicitly\nut global reporter = ut collecting reporter();\nut assert that(Expr(1 + 1), 3);\nut global reporter \u003c\u003c show report();\n```\n```jsl\n// Saves test results as rows in a JMP data table\nut global reporter = ut data table reporter();\nut test(\"Addition\", \"Addition works\", Expr( ut assert that(Expr(1 + 1), 3, \"woops\") ) );\nut global reporter \u003c\u003c New Data View;\n```\n\nAgain, most of the time, you don't need to worry about setting `ut global reporter` explicitly as the add-in handles it for you.\n\n### Using the Hamcrest Test-Runner Add-In\n\n1. Install the Add-In\n2. Write tests in a JMP script editor\n3. Attach a Test-Runner to the editor\n4. Press F5\n\n## Writing a New Matcher\n\nThe ability to create new matchers is part of what makes JSL-Hamcrest so powerful.\nYou can create matchers for your own specific use case, or contribute one back\nto the community.\n\n### UtMatcher Class\n\nThe first step is to define a class that has `UtMatcher` as its base class.\nYou just need to define the `_init_` (if needed), `matches`, and `describe` methods\nwithin your class.\n\n```jsl\n/*  Class: UtDayOfWeekMatcher\n        Checks the day of week for a date.\n*/\nDefine Class( \"UtDayOfWeekMatcher\",\n    Base Class( \"UtMatcher\" ),\n    value = .;\n    days = {\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"};\n    // Method: _init_\n    // Needed if value or inner matcher needs to be saved\n    _init_ = Method( {value},\n        this:value = value;\n    );\n    // Method: matches\n    // Return match info with success/failure info\n    matches = Method( {test expr},\n        // This evaluates the test expression\n        actual = test expr;\n        // Dates are numbers in JMP, so check the type first\n        // Name Expr() is needed to prevent actual from\n        // evaluating again in case it is an expression.\n        If( !Is Number( Name Expr( actual ) ),\n                // This returns the match info\n                ut match info failure(\n                    Eval Insert( \"^Name Expr( actual )^ was of type ^Type( Name Expr( actual ) )^\" )\n                ),\n            // If matches the correct day of week, return success\n            Day of Week( actual ) == this:value,\n                ut match info success(),\n            // else not on given day of week, return failure\n                ut match info failure(\n                    Eval Insert( \"^As Date( actual )^ was on ^this:days[Day of Week( actual )]^\" )\n                )\n        );\n    );\n\n    // Method: describe\n    // Describes the be expected outcome\n    describe = Method( {},\n        Eval Insert( \"date falling on a ^this:days[Day of Week( this:value )]^\" )\n    );\n);\n```\n\n### Matcher Factory\n\nAfter you have the matcher defined, you need to create a factory function. This\nfunction's name is what will be used in assertions. Include a call to\n`ut matcher factory` to register the matcher needed for some other matchers.\n\n```jsl\n/*  Function: ut day of week\n        Factory function for \u003cUtDayOfWeekMatcher\u003e.\n\n    Example:\n        ---JSL---\n        d = 01aug2019;\n        ut assert that( Expr( d ), ut day of week( 5 ) );\n        ---------\n*/\nut matcher factory(\n  \"ut day of week\",\n  Expr(Function( {val},\n    // Do necessary type checking of input variables\n    // in factory function.\n    If( !Is Number( val ),\n      Throw( \"ut day of week() requires a numeric date value as the argument\" ),\n    );\n    New Object( UtDayOfWeekMatcher( Name Expr( val ) ) );\n  )),\n  \"ut day of week( value )\", //Prototype\n  \"Matches based on the day of the week of a given date value.\" // Description\n);\n```\n\n### UtTypedMatcher\n\nYou can also derive from `UtTypedMatcher` to have any type checking done for you.\nThis simplifies a lot of what happens in the `matches` method.\n\n```jsl\nDefine Class( \"UtDayOfWeekMatcher\",\n    Base Class( \"UtTypedMatcher\" ),\n    value = .;\n    days = {\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"};\n    allowable types = {Date, Number};\n    _init_ = Method( {value},\n        this:value = value;\n    );\n    // Method: typed matches\n    // Handle a pre-evaluated and typed checked actual value.\n    typed matches = Method( {actual},\n        day = Day of Week( actual );\n        If( day == this:value,\n                ut match info success(),\n            // else\n                ut match info failure(\n                    Eval Insert( \"^As Date( actual )^ was on ^this:days[Day of Week( actual )]^\" )\n                )\n        );\n    );\n\n    describe = Method( {},\n        Eval Insert( \"date falling on a ^this:days[Day of Week( this:value )]^\" )\n    );\n);\n```\n\nWith deriving from `UtTypedMatcher`, you are required to inject a self reference\nonto the matcher within the factory function.\n\n```jsl\nut day of week = Function( {val},\n    If( !Is Number( val ),\n        Throw( \"ut day of week() requires a numeric date value as the argument\" ),\n    );\n    ut new object( \"UtDayOfWeekMatcher\", Eval List( {Name Expr( val )} ) );\n);\n```\n\n### Multiple Factories\n\nYou can have multiple factory functions for a single matcher to either change the\ncomparison method (like less than/greater than) or to improve the wording, such\nas `ut on thursday()` instead of `ut day of week( 5 )`.\n\n```jsl\nut on thursday = Function( {},\n    ut day of week( 5 );\n);\n```\n\n## Building Documentation\n\n1. Download [Natural Docs](naturaldocs.org)\n2. Navigate to the root of this project\n3. Run NaturalDocs\n   \u003e NaturalDocs Docs\n4. Open `Docs/_html/index.html`\n\n## Releasing a Version\n\nSee [Semantic Versioning](semver.org) for details on version numbers.\n\n1. Check `CHANGELOG.md`, `addin.def`, and `Source/DevAddin/addin.def` for correct (current) version number.\n2. Build the documentation and remove `Working Data` folder.\n3. Zip up the entire repository with `addin.def` at the root (don't include the `.git` or `.github` folder).\n4. Rename the zip file as `JSL-Hamcrest-v\u003cMAJOR\u003e.\u003cMINOR\u003e.\u003cPATCH\u003e.jmpaddin`.\n5. Zip up the `Source/DevAddin` folder with its `addin.def` at the root.\n6. Rename the zip file as `JSL-Hamcrest-Dev-v\u003cMAJOR\u003e.\u003cMINOR\u003e.\u003cPATCH\u003e.jmpaddin`.\n7. Push a tag of the form `v\u003cMAJOR\u003e.\u003cMINOR\u003e.\u003cPATCH\u003e`.\n8. Create a GitHub release. Add the relevant `CHANGELOG` section there and attach the two addins.\n9. Update the `gh-pages` branch\n10. Create a new Pull Request bumping the version number to the next predicted (see files from (1))\n\n## Support\n\nWe use GitHub for tracking bugs and feature requests. Please submit a GitHub issue or pull request for support.\n\n## Contributing\n\nWe welcome your contributions! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to submit contributions to this project.\n\n## License\n\nThis project is licensed under the [Apache 2.0 License](LICENSE).\n\n## Attributions\n\nIcons used in the JSL-Hamcrest add-in were adapted from the\n[Material Design Icons](https://github.com/google/material-design-icons)\nby Google under their\n[Apache 2.0 License](https://github.com/google/material-design-icons/blob/master/LICENSE).\n\n## Additional Resources\n\n* Original presentation materials from JMP Discovery Summit Europe 2019 can be found [here](https://community.jmp.com/t5/Discovery-Summit-Europe-2019/Automate-the-Testing-of-JSL-Using-Hamcrest-2019-EU-45MP-061/ta-p/110062).\n* [Hamcrest.org](http://hamcrest.org/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsassoftware%2Fjsl-hamcrest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsassoftware%2Fjsl-hamcrest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsassoftware%2Fjsl-hamcrest/lists"}