{"id":13758516,"url":"https://github.com/simonkowallik/tcl-smock","last_synced_at":"2026-02-05T12:34:16.879Z","repository":{"id":145419682,"uuid":"186124716","full_name":"simonkowallik/tcl-smock","owner":"simonkowallik","description":":nut_and_bolt: A simple TCL mock implementation primarily designed for unit testing of F5 iApps and tmsh scripts","archived":false,"fork":false,"pushed_at":"2019-05-25T09:09:52.000Z","size":15,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-10T22:46:31.749Z","etag":null,"topics":["mock","mocking","tcl","testing","unittest"],"latest_commit_sha":null,"homepage":"","language":"Tcl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simonkowallik.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}},"created_at":"2019-05-11T11:30:19.000Z","updated_at":"2020-01-10T23:10:35.000Z","dependencies_parsed_at":"2023-06-03T17:45:33.837Z","dependency_job_id":null,"html_url":"https://github.com/simonkowallik/tcl-smock","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/simonkowallik/tcl-smock","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonkowallik%2Ftcl-smock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonkowallik%2Ftcl-smock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonkowallik%2Ftcl-smock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonkowallik%2Ftcl-smock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simonkowallik","download_url":"https://codeload.github.com/simonkowallik/tcl-smock/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonkowallik%2Ftcl-smock/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29121787,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-05T10:47:47.471Z","status":"ssl_error","status_checked_at":"2026-02-05T10:45:08.119Z","response_time":65,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["mock","mocking","tcl","testing","unittest"],"created_at":"2024-08-03T13:00:31.646Z","updated_at":"2026-02-05T12:34:16.858Z","avatar_url":"https://github.com/simonkowallik.png","language":"Tcl","funding_links":[],"categories":["DevOps / CICD"],"sub_categories":[],"readme":"# tcl-smock\n[![Travis Build Status](https://img.shields.io/travis/com/simonkowallik/tcl-smock/master.svg?label=travis%20build)](https://travis-ci.com/simonkowallik/tcl-smock)\n[![Releases](https://img.shields.io/github/release/simonkowallik/tcl-smock.svg)](https://github.com/simonkowallik/tcl-smock/releases)\n[![Commits since latest release](https://img.shields.io/github/commits-since/simonkowallik/tcl-smock/latest.svg)](https://github.com/simonkowallik/tcl-smock/commits)\n[![Latest Release](https://img.shields.io/github/release-date/simonkowallik/tcl-smock.svg?color=blue)](https://github.com/simonkowallik/tcl-smock/releases/latest)\n\n## smock, a simple mock package\nI build smock for F5 iApp and iCall script function mocking for basic unit tests.\n\nWhen you build your own iApps or iCall scripts you often depend on commands in the tmsh:: or iapp:: namespace.\nThese again use state and information from the underlying BIG-IP system and it's configuration state.\nTherefore even simple testing can get quite complicated and time consuming, especially with future code updates.\n\nThe idea with this mocking implementation is to capture this state when you first build and test your code for later re-tests. In addition you can easily change the captured state - it's text in the end - and therefore test unexpected behaviour.\n\n`smock` is a very simple mock implementation which will basically `hijack` or simulate the output of a specific command in the given namespace, which means it will produce the output (return value) based on your mock definition.\n\n## usage in short\n```tcl\n\u003e tclsh\n\n% ls\npkgIndex.tcl smock.tcl\n\n# load package from current working directory\n% lappend auto_path \"./\"\n% package require smock 2.0\n\n# init yournamespace with proc 'function'\n% smock::init yournamespace function [function2]\n\n# mock yournamespace::function with the given parameters.\n# The last argument is always the return value of the given command\n% yournamespace::smock yournamespace::function arg1 arg2 {some return value}\n\n# test\n% yournamespace::function arg1 arg2\n% some return value\n\n# assert\n% smock::assert { true }\n% smock::assert { false }\nassertion failed: { false }\n\n% smock::assert { [yournamespace::function arg1 arg2] eq \"some return value\" }\n% smock::assert { [yournamespace::function arg1 arg2] eq \"test assert\" }\nassertion failed: { [yournamespace::function arg1 arg2] eq \"test assert\" }\n\n% catch { smock::assert { [yournamespace::function arg1 arg2] eq \"test assert\" } } catch_err\n1\n% puts $catch_err\nassertion failed: { [yournamespace::function arg1 arg2] eq \"test assert\" }\n\n# enable successful assert output\n% smock::config +verbose\n\n% smock::assert { [yournamespace::function arg1 arg2] eq \"some return value\" }\nassertion true: { [yournamespace::function arg1 arg2] eq \"some return value\" }\n% smock::assert { true }\nassertion true: { true }\n\n```\n\nAlso check the tests directory for inspiration.\n\n\n## usage by example\nLet's say you want to build a procedure to read the sync-status color of your BIG-IP setup.\nWhen using tmsh you would type `show /cm sync-status` which will provide you with the color alongside other useful information.\n\nWhen using the equivalent `tmsh::show /cm sync-status field-fmt` command, you'll get similar output to the one below. `field-fmt` is a good option for easier parsing of the output.\n```tcl\ncm cmi-sync-status {\n    color blue\n    details.0.details bigip1.example.com: disconnected\n    mode high-availability\n    status Disconnected\n    summary\n}\n```\n\nYou could create a procedure similar to this to extract the color:\n```tcl\nproc sync_status_color {} {\n  set sync_status [tmsh::show /cm sync-status field-fmt]\n  foreach line [split $sync_status \"\\n\"] {\n    if {[regexp {^\\s+color\\s(.+)$} $line ignore match]} {\n      set extracted_color $match\n    }\n  }\n  return $extracted_color\n}\n```\nThe procedure would output something like `green`, `yellow`, `red` or more uncommon and often missed `blue`, `grey` and `black`.\nTo run this procedure you would need a BIG-IP, also you would probably take a decision or do something with the return value.\n\nHere is a little example:\n```tcl\nset sync_color [sync_status_color]\nif { $sync_color eq \"yellow\" || $sync_color eq \"red\" } {\n  puts \"noo.. we can't proceed, device is not in sync!\"\n}\nelse {\n  # make some changes\n}\n```\nYou probably already noticed that a lot can go wrong with this example, we completely missed 3 sync states (blue, grey and black) and neither take care of any unepxeced response.\nSo we should probably change the code to:\n```tcl\nproc whats_the_status {} {\n  set sync_color [sync_status_color]\n  switch -- $sync_color {\n    \"green\" {\n      return \"yep ok, let's go!\"\n    }\n    \"blue\" {\n      return \"lets wait for a little while\"\n    }\n    default {\n      return \"oh nooo, something is not right!\"\n    }\n  }\n}\n```\n\nHere is some code that uses the above procedure to take a decision and do something with it:\n```tcl\nif { [whats_the_status] eq \"yep ok, let's do something!\" } {\n  return \"[sync_status_color] is good! let's make some changes!\"\n} else {\n  return \"sync_status_color says: [sync_status_color], status is: [whats_the_status]\"\n}\n```\n\nNow the question is how can we test this? We know we need a BIG-IP, but not only that, we would also need to simulate all states we'd like to use in our code decisions!\nThat can get quite complicated and time consuming, which is where `smock` can save us some time and enable us to test various cases.\n\nLet's warp the last code block in a procedure `run_example` and save everything to `example.tcl`. Then enter `tclsh`.\n\n```tcl\n# smock.tcl is one directory up, add that path and load smock 2.0\nlappend auto_path \"../\"\npackage require smock 2.0\n\n# initialize mocking for namespace 'tmsh' with a single function 'show'\nsmock::init tmsh show\n\n# now let's define the output for the command we'd like to run.\ntmsh::smock tmsh::show /cm sync-status field-fmt {cm cmi-sync-status {\n    color blue\n    details.0.details bigip1.example.com: disconnected\n    mode high-availability\n    status Disconnected\n    summary\n}}\n```\n\nLet's try it:\n```tcl\ntmsh::show /cm sync-status field-fmt\ncm cmi-sync-status {\n    color blue\n    details.0.details bigip1.example.com: disconnected\n    mode high-availability\n    status Disconnected\n    summary\n}\n\n# great, let's run our example code using 'source', which immediately executes all code in the file - hence the run_example 'wrapping' proc!\nsource example.tcl\n\nrun_example\nsync_status_color says: blue, status is: lets wait for a little while\n```\nGreat, it works. Let's proceed with assertions.\n\n```tcl\n# smock also provides you with assertions, so you can properly build tests\nsmock::assert { [run_example] eq \"sync_status_color says: blue, status is: lets wait for a little while\" }\n\n# nothing happend?\nsmock::assert { [run_example] eq \"something we expect\" }\nassertion failed: { [run_example] eq \"something we expect\" }\n\n# ok, so smock::assert { expr } it is! as run_example does not return the string \"something we expect\", it failed\n```\nNow let's test additional sync status colors (sync states):\n```tcl\n# what if the status would be red?\ntmsh::smock tmsh::show /cm sync-status field-fmt {cm cmi-sync-status {\n    color red\n    details.0.details bigip2.example.com: connected\n    details.1.details dg_syncfailover (Changes Pending): There is a possible change conflict between bigip2.example.com and bigip1.example.com.\n    details.2.details  - Recommended action: Synchronize bigip1.example.com to group dg_syncfailover\n    mode high-availability\n    status Changes Pending\n    summary There is a possible change conflict between bigip2.example.com and bigip1.example.com.\n}}\n\nrun_example\nsync_status_color says: red, status is: oh nooo, something is not right!\n\n# ok, that was epxected so tmsh:assert could look like this to express our expectation:\nsmock::assert { [string match \"*red*oh nooo*\" [run_example]] }\n\n# for more verbosity:\nsmock::config +verbose\n\nsmock::assert { [string match \"*red*oh nooo*\" [run_example]] }\nassertion true: { [string match \"*red*oh nooo*\" [run_example]] }\n```\n\nGood, that works as expected.\nBut what if somethig is strange is going on and there is no color?!\nWe'd expect to see no color in the output string but `whats_the_status` will capture that with it's default switch pattern, right?\n\n```tcl\ntmsh::smock tmsh::show /cm sync-status field-fmt {cm cmi-sync-status {\n    color\n    mode no color mode :-)\n}}\n\n# our assertion looks like this:\nsmock::assert { [run_example] eq \"sync_status_color says: , status is: oh nooo, something is not right!\" }\ncan't read \"extracted_color\": no such variable\n\n# ouch? is our assertion broken?\n\nrun_example\ncan't read \"extracted_color\": no such variable\n\n# no, it's our code!\n\n```\n\nOk, admitted you probably noticed the poor `sync_status_color` procedure before. :-)\nBut would we have found that issue in manual testing? Likely not as it is very unlikely to occur, except this one time in production.\nLet's replace the poor version with a slighty better one:\n\n```tcl\nproc sync_status_color {} {\n  set sync_status [tmsh::show /cm sync-status field-fmt]\n  foreach line [split $sync_status \"\\n\"] {\n    if {[regexp {^\\s+color\\s(.+)$} $line ignore match]} {\n      return $match\n    }\n  }\n}\n\n# run our assertion:\nsmock::assert { [run_example] eq \"sync_status_color says: , status is: oh nooo, something is not right!\" }\nassertion true: { [run_example] eq \"sync_status_color says: , status is: oh nooo, something is not right!\" }\n\n# now run the example again\nrun_example\nsync_status_color says: , status is: oh nooo, something is not right!\n```\n\nBetter :-)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimonkowallik%2Ftcl-smock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimonkowallik%2Ftcl-smock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimonkowallik%2Ftcl-smock/lists"}