{"id":18310896,"url":"https://github.com/jimschubert/mimic","last_synced_at":"2025-04-09T12:10:35.945Z","repository":{"id":63739614,"uuid":"565605444","full_name":"jimschubert/mimic","owner":"jimschubert","description":"mimic - testing terminal interactions","archived":false,"fork":false,"pushed_at":"2022-11-27T17:05:03.000Z","size":42,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-02-15T05:46:20.619Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/jimschubert/mimic","language":"Go","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/jimschubert.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}},"created_at":"2022-11-13T22:59:16.000Z","updated_at":"2023-10-14T21:01:07.000Z","dependencies_parsed_at":"2022-11-25T01:57:35.620Z","dependency_job_id":null,"html_url":"https://github.com/jimschubert/mimic","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimschubert%2Fmimic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimschubert%2Fmimic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimschubert%2Fmimic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimschubert%2Fmimic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jimschubert","download_url":"https://codeload.github.com/jimschubert/mimic/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248036066,"owners_count":21037092,"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","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":[],"created_at":"2024-11-05T16:15:48.823Z","updated_at":"2025-04-09T12:10:35.926Z","avatar_url":"https://github.com/jimschubert.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mimic - testing terminal interactions\n\n[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/jimschubert/mimic?color=%23007D9C\u0026label=github.com%2Fjimschubert%2Fmimic\u0026logo=go\u0026logoColor=white)](#installing)\n[![Go Reference](https://pkg.go.dev/badge/github.com/jimschubert/mimic.svg)](https://pkg.go.dev/github.com/jimschubert/mimic)\n![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/jimschubert/mimic?color=%23007D9C\u0026logo=go\u0026label\u0026logoColor=white)\n\n[![GitHub](https://img.shields.io/github/license/jimschubert/mimic?color=%23007D9C\u0026logo=apache\u0026label=LICENSE)](./LICENSE)\n[![Go Build](https://github.com/jimschubert/mimic/actions/workflows/build.yml/badge.svg)](https://github.com/jimschubert/mimic/actions/workflows/build.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/jimschubert/mimic)](https://goreportcard.com/report/github.com/jimschubert/mimic)\n\nMimic aims to provide a simple and clean contract for interacting with terminal contents while unit testing.\n\nThe idea arose from use of [go-survey/survey](https://github.com/go-survey/survey) and interest in using something like [charmbracelet/vhs](https://github.com/charmbracelet/vhs) for integration testing of a CLI.\nA well-crafted applications can use mimic for integration testing of CLIs with full capability to mock dependencies. This differs from integration testing with tools like vhc because mimic runs in-process.\n\nA goal of this project is to simplify testing CLIs using `survey` by providing a test suite which reduces test boilerplate and adheres to testify's suite interface(s).\n\nMimic also provides commands for:\n\n* checking existence of strings in the output buffer\n* checking existence of patterns in the output buffer\n* checking formatted output according to pseudoterminal configuration\n\n## Installing\n\nFirst, get the latest version of mimic:\n\n```shell\ngo get -u github.com/jimschubert/mimic@latest\n```\n\nThen, import mimic:\n\n```go\nimport \"github.com/jimschubert/mimic\"\n```\n\n## Usage\n\nRefer to [documentation](https://godoc.org/github.com/jimschubert/mimic) for general API and usage.\n\nFor a real-world example, see [suite_test.go](./suite/suite_test.go) in this repository.\n\n### Expect vs Contains\n\nMimic works by wrapping both [Netflix/go-expect](https://github.com/Netflix/go-expect) and [hinshun/vt10x](https://github.com/hinshun/vt10x) to provide two APIs which require a brief explanation.\n\n\n#### Expect\n\nThe expect-like APIs (`mimic.ExpectString` and `mimic.ExpectPattern`) watch the stream of stdout for a string or pattern, evaluating your criteria on each new byte written to the stream. Once criteria is met, you can't re-evaluate those same bytes. If these are concerns, consider using `mimic.ContainsString` and `mimic.ContainsPattern` which both flush pending writes (up to the flush timeout) prior to evaluating conditions.\n\nFor example, suppose you want to orchestrate the following terminal output:\n\n```\n? What is your name? Jim\n? What is your github username? jimschubert\n```\n\nThe following will work in your test:\n\n```go\nassert.NoError(t, mimic.ExpectString(\"What is your name?\"))\n```\n\nExpecting on multiple conditions for the same line will fail because it's expecting the contents to be written to stdout again. For example, expecting a partial and full line as follows does not work:\n\n```go\nassert.NoError(t, mimic.ExpectString(\"What is your name?\"))\nmimic.WriteString(\"Jim\")\n\n// can't do this with ExpectString\nassert.NoError(t, mimic.ExpectString(\"? What is your name?\"))\n```\n\nYou could use a pattern for both of these:\n\n```go\nassert.NoError(t, mimic.ExpectPattern(\"What is your.*name\"))\nmimic.WriteString(\"Jim\")\nassert.NoError(t, mimic.ExpectPattern(\"What is your.*name\"))\nmimic.WriteString(\"jimschubert\")\n```\n\nThe above example is contrived to demonstrate a concern with test performance when using ExpectPattern. **The pattern is evaluated against every new byte on the stream.** You could test this locally by adding a log message to RegexpMatcher.Match in this repository. You'd see something like this:\n\n```\nmimic: [RegexpMatcher] evaluating: ?\nmimic: [RegexpMatcher] evaluating: ? \nmimic: [RegexpMatcher] evaluating: ? W\nmimic: [RegexpMatcher] evaluating: ? Wh\nmimic: [RegexpMatcher] evaluating: ? Wha\nmimic: [RegexpMatcher] evaluating: ? What\nmimic: [RegexpMatcher] evaluating: ? What \nmimic: [RegexpMatcher] evaluating: ? What i\nmimic: [RegexpMatcher] evaluating: ? What is\nmimic: [RegexpMatcher] evaluating: ? What is \nmimic: [RegexpMatcher] evaluating: ? What is y\nmimic: [RegexpMatcher] evaluating: ? What is yo\nmimic: [RegexpMatcher] evaluating: ? What is you\nmimic: [RegexpMatcher] evaluating: ? What is your\nmimic: [RegexpMatcher] evaluating: ? What is your \nmimic: [RegexpMatcher] evaluating: ? What is your n\nmimic: [RegexpMatcher] evaluating: ? What is your na\nmimic: [RegexpMatcher] evaluating: ? What is your nam\nmimic: [RegexpMatcher] evaluating: ? What is your name\nmimic: [RegexpMatcher] evaluating: ?\n```\n\nIf you wanted to validate that `What is your name?` is not output twice, expecting via `assert.Error` will run until the timeout period. Suppose you have a timeout of 5 seconds (constructed with `mimic.WithIdleTimeout(5 * time.Second)`). The following `assert.Error` is technically valid, but adds 5 seconds to your test function:\n\n```go\nassert.NoError(t, mimic.ExpectString(\"What is your name?\"))\nmimic.WriteString(\"Jim\")\nassert.Error(t, mimic.ExpectString(\"What is your name?\"), \"This condition should succeed after 5 seconds!\")\n```\n\nOnce you're done with all interactions, it's a best practice to invoke `mimic.NoMoreExpectations()`. This flushes remaining bytes to stdout and expects `io.EOF` on the stream.\n\n## Contains\n\nThe contains APIs (`mimic.ContainsString` and `mimic.ContainsPattern`) both flush pending writes (up to the flush timeout) prior to evaluating conditions.\n\nFor example, suppose you want to orchestrate the following terminal output:\n\n```\n? What is your name? Jim\n? What is your github username? jimschubert\n```\n\nThe following will work in your test:\n\n```go\nassert.True(t, mimic.ContainsString(\"What is your name?\"))\n```\n\nThe test case described in the [Expect](#expect) second which failed would work using ContainsString:\n\n```go\nassert.True(t, mimic.ContainsString(\"What is your name?\"))\nmimic.WriteString(\"Jim\")\n\n// can do this with ContainsString, but not ExpectString\nassert.True(t, mimic.ContainsString(\"? What is your name?\"))\n```\n\n`ContainsString` and `ContainsPattern` work on the full terminal view. Keep this in mind as the following which works serially in the Expect API works a little differently:\n\n```go\nassert.True(t, mimic.ContainsPattern(\"What is your.*name\"))\nmimic.WriteString(\"Jim\")\n\n// WARNING: Sill passes for \"What is your name?\" even if \"What is your github username?\" is never displayed\nassert.True(t, mimic.ContainsPattern(\"What is your.*name\"))\nmimic.WriteString(\"jimschubert\")\n```\n\nSince `ContainsPattern` works on the entire view contents, each regex passed to the function is evaluated once. If you were to add a logger within the `ContainsPattern` function, you'd see a trace similar to (one log for each of the above assertions):\n\n```\nmimic: [ContainsPattern] evaluating: What is your.*name\nmimic: [ContainsPattern] evaluating: What is your.*name\n```\n\nYou can always mix and match these APIs:\n\n```go\nassert.True(t, mimic.ContainsPattern(\"What is your.*name\"))\nmimic.WriteString(\"Jim\")\nassert.NoError(t, mimic.ExpectPattern(\"What is your.*name\"))\nmimic.WriteString(\"jimschubert\")\n```\n\n**Prefer `ContainsString` or `ExpectString` over pattern based functions where possible.\n\n## License\n\nThis project is [licensed](./LICENSE) under Apache 2.0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjimschubert%2Fmimic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjimschubert%2Fmimic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjimschubert%2Fmimic/lists"}