Skip to content

Commit 1533875

Browse files
author
Aki Arvio
committed
feat: Add run() wrapper with command output assertions
Add a new test pattern for asserting command outputs and exit status through a run() wrapper function and corresponding assertion functions. Addresses issue: #134 -------------------------------------- User story suggested pipe syntax: command_under_test | should_display "${expected_pattern}" command_under_test | err_should_contain "${expected_pattern}" Implemented wrapper pattern: run command_under_test && assert_run_output_matches "${expected_pattern}" run command_under_test && assert_run_error_matches "${expected_pattern}" Works also as separate lines: run command_under_test assert_run_output_matches "${expected_pattern}" assert_run_error_matches "${expected_pattern}" The wrapper pattern achieves the same readability while providing more flexibility and reliability. Problem with pipes: - pipes carry only stdout - harder to resolve exit status if needed - can't verify multiple conditions independently Usage Examples -------------- Simple one-liners: run my_script && assert_run_success run my_command && assert_run_output_equals "expected" run failing_cmd && assert_run_status_code 42 Multiple assertions: run my_script.sh input.txt assert_run_success assert_run_output_matches "^Processed.*successfully" assert_run_error_equals ""
1 parent 844a6cd commit 1533875

3 files changed

Lines changed: 500 additions & 1 deletion

File tree

README.adoc

Lines changed: 295 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ The following functions are available in your tests (see below for detailed docu
3636
* `assert_not_matches <unexpected-regex> <actual> [message]`
3737
* `assert_within_delta <expected num> <actual num> <max delta> [message]`
3838
* `assert_no_diff <expected> <actual> [message]`
39+
* `run <command> [args...]`
40+
* `assert_run_success [message]`
41+
* `assert_run_fails [message]`
42+
* `assert_run_status_code <expected_status_code> [message]`
43+
* `assert_run_output_equals <expected> [message]`
44+
* `assert_run_output_matches <expected-regex> [message]`
45+
* `assert_run_error_equals <expected> [message]`
46+
* `assert_run_error_matches <expected-regex> [message]`
3947
* `skip_if <condition> <pattern>`
4048
* `fake <command> [replacement code]`
4149

@@ -189,6 +197,22 @@ Running tests in tests/test_core.sh
189197
Running test_assert_not_equals_succeeds_when_not_equal ... SUCCESS
190198
Running test_assert_not_matches_fails_when_matching ... SUCCESS
191199
Running test_assert_not_matches_succeed_when_not_matching ... SUCCESS
200+
Running test_assert_run_error_equals_fails_when_mismatch ... SUCCESS
201+
Running test_assert_run_error_equals_succeeds ... SUCCESS
202+
Running test_assert_run_error_matches_fails_when_no_match ... SUCCESS
203+
Running test_assert_run_error_matches_succeeds ... SUCCESS
204+
Running test_assert_run_fails_fails_when_zero ... SUCCESS
205+
Running test_assert_run_fails_succeeds ... SUCCESS
206+
Running test_assert_run_output_equals_fails_when_mismatch ... SUCCESS
207+
Running test_assert_run_output_equals_handles_multiline ... SUCCESS
208+
Running test_assert_run_output_equals_succeeds ... SUCCESS
209+
Running test_assert_run_output_matches_fails_when_no_match ... SUCCESS
210+
Running test_assert_run_output_matches_succeeds ... SUCCESS
211+
Running test_assert_run_status_code_fails_when_mismatch ... SUCCESS
212+
Running test_assert_run_status_code_succeeds ... SUCCESS
213+
Running test_assert_run_success_fails_when_nonzero ... SUCCESS
214+
Running test_assert_run_success_shows_custom_message ... SUCCESS
215+
Running test_assert_run_success_succeeds ... SUCCESS
192216
Running test_assert_shows_stderr_on_failure ... SUCCESS
193217
Running test_assert_shows_stdout_on_failure ... SUCCESS
194218
Running test_assert_status_code_fails ... SUCCESS
@@ -205,6 +229,10 @@ Running tests in tests/test_core.sh
205229
Running test_fake_exports_faked_in_subshells ... SUCCESS
206230
Running test_fake_transmits_params_to_fake_code ... SUCCESS
207231
Running test_fake_transmits_params_to_fake_code_as_array ... SUCCESS
232+
Running test_run_clears_previous_state ... SUCCESS
233+
Running test_run_combined_assertions ... SUCCESS
234+
Running test_run_handles_command_with_arguments ... SUCCESS
235+
Running test_run_handles_commands_with_pipes ... SUCCESS
208236
Running test_should_pretty_format_even_when_LANG_is_unset ... SUCCESS
209237
Running test_should_pretty_format_even_with_pipefail_set ... SUCCESS
210238
Overall result: SUCCESS
@@ -233,6 +261,22 @@ Running tests in tests/test_core.sh
233261
Running test_assert_not_equals_succeeds_when_not_equal ... SUCCESS
234262
Running test_assert_not_matches_fails_when_matching ... SUCCESS
235263
Running test_assert_not_matches_succeed_when_not_matching ... SUCCESS
264+
Running test_assert_run_error_equals_fails_when_mismatch ... SUCCESS
265+
Running test_assert_run_error_equals_succeeds ... SUCCESS
266+
Running test_assert_run_error_matches_fails_when_no_match ... SUCCESS
267+
Running test_assert_run_error_matches_succeeds ... SUCCESS
268+
Running test_assert_run_fails_fails_when_zero ... SUCCESS
269+
Running test_assert_run_fails_succeeds ... SUCCESS
270+
Running test_assert_run_output_equals_fails_when_mismatch ... SUCCESS
271+
Running test_assert_run_output_equals_handles_multiline ... SUCCESS
272+
Running test_assert_run_output_equals_succeeds ... SUCCESS
273+
Running test_assert_run_output_matches_fails_when_no_match ... SUCCESS
274+
Running test_assert_run_output_matches_succeeds ... SUCCESS
275+
Running test_assert_run_status_code_fails_when_mismatch ... SUCCESS
276+
Running test_assert_run_status_code_succeeds ... SUCCESS
277+
Running test_assert_run_success_fails_when_nonzero ... SUCCESS
278+
Running test_assert_run_success_shows_custom_message ... SUCCESS
279+
Running test_assert_run_success_succeeds ... SUCCESS
236280
Running test_assert_shows_stderr_on_failure ... SUCCESS
237281
Running test_assert_shows_stdout_on_failure ... SUCCESS
238282
Running test_assert_status_code_fails ... SUCCESS
@@ -241,6 +285,7 @@ Running tests in tests/test_core.sh
241285
Running test_assert_within_delta_fails ... SUCCESS
242286
Running test_assert_within_delta_succeeds ... SUCCESS
243287
Running test_fail_fails ... SUCCESS
288+
Running test_run_combined_assertions ... SUCCESS
244289
Overall result: SUCCESS
245290
```
246291

@@ -261,19 +306,41 @@ Running tests in tests/test_core.sh
261306
Running test_assert_not_equals_succeeds_when_not_equal ... SKIPPED
262307
Running test_assert_not_matches_fails_when_matching ... SKIPPED
263308
Running test_assert_not_matches_succeed_when_not_matching ... SKIPPED
309+
Running test_assert_run_error_matches_fails_when_no_match ... SKIPPED
310+
Running test_assert_run_output_matches_fails_when_no_match ... SKIPPED
311+
Running test_assert_run_status_code_fails_when_mismatch ... SKIPPED
312+
Running test_assert_run_status_code_succeeds ... SKIPPED
313+
Running test_assert_run_success_fails_when_nonzero ... SKIPPED
264314
Running test_assert_status_code_fails ... SKIPPED
265315
Running test_assert_status_code_succeeds ... SKIPPED
266316
Running test_assert_equals_succeed_when_equal ... SUCCESS
267317
Running test_assert_fails ... SUCCESS
268318
Running test_assert_fails_fails ... SUCCESS
269319
Running test_assert_fails_succeeds ... SUCCESS
270320
Running test_assert_matches_succeed_when_matching ... SUCCESS
321+
Running test_assert_run_error_equals_fails_when_mismatch ... SUCCESS
322+
Running test_assert_run_error_equals_succeeds ... SUCCESS
323+
Running test_assert_run_error_matches_succeeds ... SUCCESS
324+
Running test_assert_run_fails_fails_when_zero ... SUCCESS
325+
Running test_assert_run_fails_succeeds ... SUCCESS
326+
Running test_assert_run_output_equals_fails_when_mismatch ... SUCCESS
327+
Running test_assert_run_output_equals_handles_multiline ... SUCCESS
328+
Running test_assert_run_output_equals_succeeds ... SUCCESS
329+
Running test_assert_run_output_matches_succeeds ... SUCCESS
330+
Running test_assert_run_success_shows_custom_message ... SUCCESS
331+
Running test_assert_run_success_succeeds ... SUCCESS
271332
Running test_assert_shows_stderr_on_failure ... SUCCESS
272333
Running test_assert_shows_stdout_on_failure ... SUCCESS
273334
Running test_assert_succeeds ... SUCCESS
274335
Running test_assert_within_delta_fails ... SUCCESS
275336
Running test_assert_within_delta_succeeds ... SUCCESS
276337
Running test_fail_fails ... SUCCESS
338+
Running test_run_combined_assertions ... SUCCESS
339+
Overall result: SUCCESS
340+
```
341+
Running test_assert_within_delta_fails ... SUCCESS
342+
Running test_assert_within_delta_succeeds ... SUCCESS
343+
Running test_fail_fails ... SUCCESS
277344
Overall result: SUCCESS
278345
```
279346

@@ -299,6 +366,22 @@ ok - test_assert_not_equals_fails_when_equal
299366
ok - test_assert_not_equals_succeeds_when_not_equal
300367
ok - test_assert_not_matches_fails_when_matching
301368
ok - test_assert_not_matches_succeed_when_not_matching
369+
ok - test_assert_run_error_equals_fails_when_mismatch
370+
ok - test_assert_run_error_equals_succeeds
371+
ok - test_assert_run_error_matches_fails_when_no_match
372+
ok - test_assert_run_error_matches_succeeds
373+
ok - test_assert_run_fails_fails_when_zero
374+
ok - test_assert_run_fails_succeeds
375+
ok - test_assert_run_output_equals_fails_when_mismatch
376+
ok - test_assert_run_output_equals_handles_multiline
377+
ok - test_assert_run_output_equals_succeeds
378+
ok - test_assert_run_output_matches_fails_when_no_match
379+
ok - test_assert_run_output_matches_succeeds
380+
ok - test_assert_run_status_code_fails_when_mismatch
381+
ok - test_assert_run_status_code_succeeds
382+
ok - test_assert_run_success_fails_when_nonzero
383+
ok - test_assert_run_success_shows_custom_message
384+
ok - test_assert_run_success_succeeds
302385
ok - test_assert_shows_stderr_on_failure
303386
ok - test_assert_shows_stdout_on_failure
304387
ok - test_assert_status_code_fails
@@ -315,9 +398,13 @@ ok - test_fake_echo_stdin_when_no_params
315398
ok - test_fake_exports_faked_in_subshells
316399
ok - test_fake_transmits_params_to_fake_code
317400
ok - test_fake_transmits_params_to_fake_code_as_array
401+
ok - test_run_clears_previous_state
402+
ok - test_run_combined_assertions
403+
ok - test_run_handles_command_with_arguments
404+
ok - test_run_handles_commands_with_pipes
318405
ok - test_should_pretty_format_even_when_LANG_is_unset
319406
ok - test_should_pretty_format_even_with_pipefail_set
320-
1..31
407+
1..51
321408
```
322409

323410
== How to write tests
@@ -659,6 +746,213 @@ out> > bar
659746
doc:2:test_obvious_notmatching_with_assert_no_diff()
660747
```
661748

749+
=== *run*
750+
751+
run <command> [args...]
752+
753+
Executes _command_ with the given arguments and captures:
754+
755+
* Exit status in `$BASH_UNIT_RUN_STATUS`
756+
* Standard output in `$BASH_UNIT_RUN_OUTPUT`
757+
* Standard error in `$BASH_UNIT_RUN_ERROR`
758+
759+
Each call to `run` clears the previous values, so these variables always contain the results of the most recent command.
760+
761+
Use this function in combination with the `assert_run_*` functions to test command behavior.
762+
763+
=== *assert_run_success*
764+
765+
assert_run_success [message]
766+
767+
Asserts that the last command executed with `run` succeeded (exit status 0).
768+
769+
```test
770+
test_command_succeeds(){
771+
run echo "hello"
772+
assert_run_success
773+
}
774+
775+
test_command_fails_when_expected_success(){
776+
run false
777+
assert_run_success
778+
}
779+
```
780+
781+
```output
782+
Running test_command_fails_when_expected_success ... FAILURE
783+
expected run command to succeed but status was 1
784+
doc:8:test_command_fails_when_expected_success()
785+
Running test_command_succeeds ... SUCCESS
786+
```
787+
788+
=== *assert_run_fails*
789+
790+
assert_run_fails [message]
791+
792+
Asserts that the last command executed with `run` failed (exit status not 0).
793+
794+
```test
795+
test_command_fails(){
796+
run false
797+
assert_run_fails
798+
}
799+
800+
test_command_succeeds_when_expected_failure(){
801+
run true
802+
assert_run_fails
803+
}
804+
```
805+
806+
```output
807+
Running test_command_fails ... SUCCESS
808+
Running test_command_succeeds_when_expected_failure ... FAILURE
809+
expected run command to fail but status was 0
810+
doc:8:test_command_succeeds_when_expected_failure()
811+
```
812+
813+
=== *assert_run_status_code*
814+
815+
assert_run_status_code <expected_status_code> [message]
816+
817+
Asserts that the last command executed with `run` exited with a specific status code.
818+
819+
```test
820+
test_specific_exit_code(){
821+
run sh -c 'exit 42'
822+
assert_run_status_code 42
823+
}
824+
825+
test_wrong_exit_code(){
826+
run sh -c 'exit 1'
827+
assert_run_status_code 0
828+
}
829+
```
830+
831+
```output
832+
Running test_specific_exit_code ... SUCCESS
833+
Running test_wrong_exit_code ... FAILURE
834+
expected run command to have status code 0 but was 1
835+
doc:8:test_wrong_exit_code()
836+
```
837+
838+
=== *assert_run_output_equals*
839+
840+
assert_run_output_equals <expected> [message]
841+
842+
Asserts that the standard output from the last `run` command exactly matches _expected_.
843+
844+
```test
845+
test_output_matches(){
846+
run echo "hello world"
847+
assert_run_output_equals "hello world"
848+
}
849+
850+
test_output_differs(){
851+
run echo "actual"
852+
assert_run_output_equals "expected"
853+
}
854+
```
855+
856+
```output
857+
Running test_output_differs ... FAILURE
858+
expected run output [expected] but was [actual]
859+
doc:8:test_output_differs()
860+
Running test_output_matches ... SUCCESS
861+
```
862+
863+
=== *assert_run_output_matches*
864+
865+
assert_run_output_matches <expected-regex> [message]
866+
867+
Asserts that the standard output from the last `run` command matches the given regex pattern.
868+
869+
```test
870+
test_output_matches_pattern(){
871+
run echo "Error: file not found"
872+
assert_run_output_matches "Error:.*not found"
873+
}
874+
875+
test_output_does_not_match(){
876+
run echo "Success"
877+
assert_run_output_matches "Error:"
878+
}
879+
```
880+
881+
```output
882+
Running test_output_does_not_match ... FAILURE
883+
expected run output to match regex [Error:] but was [Success]
884+
doc:8:test_output_does_not_match()
885+
Running test_output_matches_pattern ... SUCCESS
886+
```
887+
888+
=== *assert_run_error_equals*
889+
890+
assert_run_error_equals <expected> [message]
891+
892+
Asserts that the standard error from the last `run` command exactly matches _expected_.
893+
894+
```test
895+
test_error_matches(){
896+
run sh -c 'echo "error message" >&2'
897+
assert_run_error_equals "error message"
898+
}
899+
900+
test_error_differs(){
901+
run sh -c 'echo "actual error" >&2'
902+
assert_run_error_equals "expected error"
903+
}
904+
```
905+
906+
```output
907+
Running test_error_differs ... FAILURE
908+
expected run error [expected error] but was [actual error]
909+
doc:8:test_error_differs()
910+
Running test_error_matches ... SUCCESS
911+
```
912+
913+
=== *assert_run_error_matches*
914+
915+
assert_run_error_matches <expected-regex> [message]
916+
917+
Asserts that the standard error from the last `run` command matches the given regex pattern.
918+
919+
```test
920+
test_error_matches_pattern(){
921+
run sh -c 'echo "Warning: disk space low" >&2'
922+
assert_run_error_matches "Warning:.*low"
923+
}
924+
925+
test_error_does_not_match(){
926+
run sh -c 'echo "Info: all good" >&2'
927+
assert_run_error_matches "Warning:"
928+
}
929+
```
930+
931+
```output
932+
Running test_error_does_not_match ... FAILURE
933+
expected run error to match regex [Warning:] but was [Info: all good]
934+
doc:8:test_error_does_not_match()
935+
Running test_error_matches_pattern ... SUCCESS
936+
```
937+
938+
=== Combined usage example
939+
940+
You can combine multiple assertions to thoroughly test a command's behavior:
941+
942+
```test
943+
test_complete_command_verification(){
944+
run sh -c 'echo "output"; echo "error" >&2; exit 0'
945+
946+
assert_run_success
947+
assert_run_output_equals "output"
948+
assert_run_error_equals "error"
949+
}
950+
```
951+
952+
```output
953+
Running test_complete_command_verification ... SUCCESS
954+
```
955+
662956
== *skip_if* function
663957

664958
skip_if <condition> <pattern>

0 commit comments

Comments
 (0)