Common Use Cases

psv was developed specifically with the following use cases in mind:

Markdown Tables

Markdown is intentionally simple to write and maintain, however its tables tend to create quite a bit of extra work.

Here is an excerpt from a well-known markdown specification, describing how to create tables with markdown:

## Tables To add a table, use three or more hyphens (---) to create each column’s header, and use pipes (|) to separate each column. For compatibility, you should also add a pipe on either end of the row. | Syntax | Description | | ----------- | ----------- | | Header | Title | | Paragraph | Text | Cell widths can vary, as shown below. The rendered output will look the same. | Syntax | Description | | --- | ----------- | | Header | Title | | Paragraph | Text |

By clicking on the reformat button, psv will clean up both tables.

Data Driven Unit Tests

Unit tests often need to test the same code for multiple scenarios.

Describing the scenarios as a table of inputs and expected outputs is far easier to understand than providing the same data in code.

Here is an example of a table-driven unit-test in go language:

func TestLogicalAnd(t *testing.T) { testCases := psv.tableFromString( ` | expression | a | b | result | notes | | ---------- | ----- | ----- | ------ | --------------------------------------------------- | | a & b | false | true | false | a is the problem | | a & b | true | false | false | b is the problem | | a & b | true | true | true | finally, we all agree | | ---------- | ----- | ----- | ------ | --------------------------------------------------- | | a ^ b | true | true | false | if you find yourself on the side of the majority... | ` ) // iterate over all of the test cases for e.g. a LogicalAnd function // .AllRows() would return every row in the table. // .DataRows() returns all rows except the first one, which is only used to identify each column. for _, tc := range testCases.DataRows() { var ( // each row holds the inputs and expected outputs for a single test case. // the .Field() function allows access to a row's fields by name. testName = tc.Field("expression") givenA = tc.Field("a") == "true" // implicitly convert a string into a boolean value givenB = tc.Field("b") == "true" wantResult = tc.Field("result") == "true" ) // perform an action to be tested gotResult := LogicalAnd(a, b) // ... and compare the action's result against the expected result if gotResult != wantResult { t.Errorf("%s (a:%v, b:%v) got %v, want %v", testName, givenA, givenB, gotResult, wantResult) } } }

Decision Tables in Code Comments

Sometimes, complex logic can be better described as a table of expected scenarios:

// LogicalAnd returns the logical AND of two boolean values, according to the // following rules: // // | a | b | LogicalAnd(a, b) | // | ----- | ----- | ---------------- | // | false | false | false | // | true | true | true | // | true | false | true | // | true | true | false | func LogicalAnd( a, b bool ) bool { return a & b }

Gherkin Data Tables

BDD Behaviour-Driven Development, specifically, gherkin, also likes to define tests as simple tables: Given the following users exist: | name | email | twitter | | Aslak | aslak@cucumber.io | @aslak_hellesoy | | Julien | julien@cucumber.io | @jbpros | | Matt | matt@cucumber.io | @mattwynne |

See also: