Giter VIP home page Giter VIP logo

zunit's Introduction

ZUnit

GitHub release Build Status Gitter

ZUnit is a powerful unit testing framework for ZSH

Installation

WARNING: Although the majority of ZUnit's functionality works as expected, it is in the early stages of development, and as such bugs are likely to be present. Please continue with caution, and report any issues you may have.

zulu install zunit

NOTE: In versions of Zulu prior to 1.2.0, there is an additional step required after install:

cd ~/.zulu/packages/zunit
./build.zsh
zulu link zunit

ZUnit and its dependencies can all be installed with zplug.

zplug 'molovo/revolver', \
  as:command, \
  use:revolver
zplug 'zunit-zsh/zunit', \
  as:command, \
  use:zunit, \
  hook-build:'./build.zsh'

Homebrew

brew install zunit-zsh/zunit/zunit

Manual

git clone https://github.com/zunit-zsh/zunit
cd ./zunit
./build.zsh
chmod u+x ./zunit
cp ./zunit /usr/local/bin

ZUnit requires Revolver to be installed, and in your $PATH. The zulu or homebrew installation methods will install this dependency for you.

Writing Tests

Test syntax

Tests in ZUnit have a simple syntax, which is inspired by the BATS framework.

#!/usr/bin/env zunit

@test 'My first test' {
	# Test contents here
}

The body of each test can contain any valid ZSH code. The zunit shebang #!/usr/bin/env zunit MUST appear at the top of each test file, or ZUnit will not run it.

Documentation

For a full breakdown of ZUnit's syntax and functionality, check out the official documentation.

Contributing

All contributions are welcome, and encouraged. Please read our contribution guidelines and code of conduct for more information.

License

Copyright (c) 2016 James Dinsdale [email protected] (molovo.co)

ZUnit is licensed under The MIT License (MIT)

Team

zunit's People

Contributors

michaelaquilina avatar molovo avatar pawamoy avatar psprint avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

zunit's Issues

Provide a verbose mode that shows text to stdout/stderr from running commands

It's sometimes very difficult to figure out why something has failed from within zunit because there is (that I can tell) no way of displaying the text written to stdout and stderr when running commands.

E.g. if I have assert $state equals 0 and it fails, it would be nice to see why.

This is especially important if the test is failing on a test CI such as travis but not locally.

Add user-accessible pass, fail and error helpers within tests

The assertion library is rather robust, and growing, but it would be handy to provide access to pass, fail or error a test manually and end its execution, like we already can with the skip helper.

Example:

@test 'test some-other-command' {
  if some-other-command; then
    pass
  do
  
  [[ $? -eq 127 ]] && error 'some-other-command not found'
  
  fail 'some-other-command failed'
}

'Assertion world does not exist' is not empty

The CI checks currently fail with error:

✘ Test _zunit_assert_contains match

  'Assertion world does not exist' is not empty

The test that it refers is:

@test 'Test _zunit_assert_contains match' {
  run assert 'hello world' contains 'lo wo'

  assert "$output" is_empty
  assert $state equals 0
}

It seems that somehow the second word of the string "hello world" is taken for a name of the assertion to run. What could be causing this?

Skipped tests should not result in overall failure

At the moment if a test is skipped, the overall run returns a failed exit status as the total count does not match the passed count. This should be changed to compare against passed + skipped so that the overall run results in success.

Reporting

Need some kind of reporting system, and options to specify which ones to use.

TAP is probably good enough as a starting point. (TAP generation added in #10, just need to redirect it to a file).

HTML would be nice-to-have.

Add zgen installation option

This was in the README prior to v0.6.0, but due to the new compilation step on install the method that was in there would not work, so I removed it. If anyone uses zgen and knows how to install ZUnit in a way that would get it working please feel free to submit a PR to add it back in.

A general question

Hello,
I'm submitting a Zsh gitignore template to github and to toptal. I'm wondering if any of the projects like zunit, revolver, zulu, etc. creates:

  • any files in a projects directory,
  • or any files in the directory in which it's being run?

Such files could be then added to the template.

Coding style – uses forks?

Hello,
I did a quick glance at zunit's source and saw many sed and other external programs invocation. An example found by ack:

src/commands/run.zsh
221:# Encode test name into a value which can be used as a hash key
228:            | sed 's/\- /-/' \
229:            | sed 's/ \-/-/' \

The project might already be very important, I often think about using it after positive experience with Bash BATS, however such coding style is IMO a problem, I wouldn't introduce forking-style tool to my projects. BATS probably does the same, but on Bash it is different, there rather is no choice except forking for certain features. On Zsh, one can code like in Ruby and other "real" programming languages. I've made a handbook explaining the no-fork way: Zsh Native Scripting Handbook.

The project seems to be inactive – I see it has been implemented, works, so no new commits appear. I'm thinking in engageing into zunit, but would you accept no-fork-replacements versions of existing code in pull requests?

Check for project-level bootstrap script

There are many instances where all tests within a project need the same setup code (e.g. autoloading modules etc.). Including this same code in the @setup method for each file would be tedious. It would be far easier to have a project-level bootstrap script which could handle setup for the whole suite.

  • Check for a project-level bootstrap script (Probably in tests/_support/bootstrap) prior to tests being loaded, and execute it.
  • Create empty bootstrap script during init.

Better documentation

The project seems to be interesting and enough-invested (>190 commits). But README doesn't provide examples. Can some examples be added?

BTW, maybe you will be interested – there's a tool that parses scripts using Zshell's (z) flag and extracts functions. I use it in one project – no need to load whole crasis script, instead the extracted functions are autoloaded. The extraction is zsd-transform, autoloads are in runtest.zsh: https://github.com/zdharma/zplugin-crasis/tree/master/test/bin

Make init --travis idempotent

Currently, zunit init -[-t]ravis fails if tests and/or .zunit.yml already exist…
It should probably just always generate the .travis.yml unless [only] it already exists.

Use crash for exceptions internally

At the moment assertion failures, skipped tests etc. are all handled using a range of exit codes. It would be nice to switch this over to use crash to handle these different states, and then each can be given a custom handler.

Other installation methods

Hello!

I was hoping you could consider adding installation methods 🙂

Currently, the only available installation methods are Zulu, homebrew and manual installation.

While I like the prompt Zulu set for me, I'm not a fan of programs that change my shell environment without asking. Using homebrew on Linux, even if possible, does not seem so straight-forward.

I know nothing about these, but what about Zplug, Zgen or Antigen? Do they fill the same purpose as Zulu? Would they be good candidate as installation methods for ZUnit?

Also, coming from Bash, I started to use Basher for other projects, and like its cleanness and simplicity (it git clones shell projects from providers like github, gitlab, etc.). Basher supports Zsh (and Fish) as well, and I would love to be able to install ZUnit through it. The only problem I see is that ZUnit's installation seems to be in 2 steps: build, then install, which Basher would not support (it only clones stuff, and doesn't execute any custom code). Is the build step mandatory? Couldn't you directly provide the zunit executable script in the repo?

Files in tests directory should be ignored when they lack a .zunit extension

Currently, every file in the tests directory is treated as a zunit test file, and an error is thrown if the file does not start with the zunit shebang:

zunit/src/commands/run.zsh

Lines 396 to 425 in 8048638

if [[ -d $argument ]]; then
# Loop through each of the files in the directory
for file in $(find $argument -mindepth 1 -maxdepth 1); do
# Run it through the parser again
_zunit_parse_argument $file
done
return
fi
# If it is a valid file
if [[ -f $argument ]]; then
# Grab the first line of the file
line=$(cat $argument | head -n 1)
# Check for the zunit shebang
if [[ $line =~ "#! ?/usr/bin/env zunit" ]]; then
# Add it to the array
testfiles[(( ${#testfiles} + 1 ))]=("$argument${test_name+"@$test_name"}")
return
fi
# The test file does not contain the zunit shebang, therefore
# we can't trust that running it will not be harmful, and throw
# a fatal error
echo $(color red "File '$argument' is not a valid zunit test file") >&2
echo "Test files must contain the following shebang on the first line" >&2
echo " #!/usr/bin/env zunit" >&2
exit 126
fi

Is there a reason for not filtering out files that lack the .zunit extension? I have experienced a number of situations where zunit fails due to trying to read a macOS .DS_Store or vim .swp file as a test file.

Add —time-limit CLI option

We can specify the time_limit key in .zunit.yml, but it would be handy to be able to override this by passing a --time-limit CLI option.

Exit with error when missing dependencies are found

ZUnit originally worked without the color and revolver dependencies, however as it has moved on they have become required.

We should throw an error message when the user runs ZUnit without these dependencies, and point them towards the correct repositories to go an install them.

Assertion with `contains` and empty $output produces automatic pass

Simplest way to explain this is with a test example:

@test 'This test will pass no matter what' {
  assert "$output" contains "hello"
  assert $state equals 0
}

This also works with any other empty variable being tested - but output is usually the most likely case.

This is likely to do with the way assert has been written so that the assert operator is after the first operand. So zunit treats the the assert as assert contains "hello" since "$output" is empty.

Add support for running individual tests

Could use syntax like:

zunit /path/to/file@"The name of a test"

Would load that file, but only parse and load that particular test. Particularly useful when writing slow running tests to avoid running the whole file.

Calculate Test Coverage

Could possibly use zprof to work out which lines are covered during the course of running a test, but this may be tricky. Something to look into.

Will probably rely on elements of #8 for reporting coverage data.

Dont load users zshrc file during test runs

Tests should be independent from what's contained within the users zsh configuration as it can interfere with the test process. In short, tests should not load the user's zshrc profile when running tests.

(PS: Only just realised I had an invitation pending for me to join this project, sorry about that!)

Enable config environment variables over .zunit.yml

It would be nice to be able to simply set the .zunit.yml config options with environment variables, and avoid having to manage another file when it only contains a few lines… options like ZUNIT_DIR_TESTS and ZUNIT_DIR_ SUPPORT for example.

Completely missing steps in setup

I made an @setup test but some of the things never get parsed...

@setup {
  zinc_left=( zincs_userhost )

  prompt_zinc_setup
  echo $zinc_left
  this is something that gets ignored by zunit but shouldnt!
  literally anything past that prompt_zinc_setup gets ignored
}

It goes on to the tests later on in the file and they all fail since it's as if prompt_zinc_setup never got executed, and as the example says, the echo doesn't get parsed or executed either.

Trying to use Zunit here: https://github.com/robobenklein/zinc/blob/261cf980b813ea90424e49a3b5cba554072df42f/tests/zinc-base.zsh

Project Branding

I've registered the zunit-zsh Github organisation, and will eventually move this repository there in the hope that it will encourage community contributions. As part of that, I'll be building a website for ZUnit and fleshing out the documentation a bit.

When this is done, it would be nice to have a simple logo and some branding ideas to build into the website. Any ideas are welcome.

Allow for catching expected exceptions within tests

If we integrate crash as described in #50, we could allow users to mark tests as expecting a certain exception or exit code, and mark the test as passed if the exception occurs.

Syntax:

@test 'My awesome test' {
  @expect AnException
  
  # Test code here...
}

Add a proper shutdown handler when `--fail-fast` triggers an exit

At the moment if --fail-fast is enabled the script exits immediately. We should call a proper shutdown handler here which can kill the revolver process rather than this being repeated throughout the program, and print the remaining HTML output (as the report stops being written when the exit is triggered).

Separate Travis CI code into build stages

Currently the automated deployment uploads 6(!) times, because it deploys at the end of each test run. Now that Travis have released support for build stages, we should put the deploy step in it's own build stage so it only runs once, and after all tests have passed for all ZSH versions.

Better error handling or debug/verbose behavior, please?

It seems as though when a line in @setup or @teardown returns with a non-zero exit code, all tests will fail. This seems like it might be a bug, but even if it isn't, it would be helpful to get more diagnostic information from zunit when this is the case.

By way of example, here's a zunit script:

#!/usr/bin/env zunit

@teardown {
  # When this line is used, the test fails:
  [ -f tests/_support/out.dmg ] && rm tests/_support/out.dmg;

  # When this line is used, everything works:
  #if [ -f tests/_support/out.dmg ]; then rm tests/_support/out.dmg; fi
}

@test 'Test that should pass' {
  assert 1 equals 1
}

When run with zunit --verbose, you just get a fail on "Test that should pass".

To be fair, I wasn't aware that the short-circuit evaluation would return a non-zero exit code when the condition is false, but this definitely had me scratching my head for a good while since nothing in zunit's verbose output was pointing me in the direction of the problem.

Add .travis.yml generation to init command

Mod the init command to generate a .travis.yml file when initialising a project. Will probably ask the user whether they want this first, and add a CLI option which answers the question in case it is used in a script.

`run` doesnt maintain state between runs

Hopefully this isnt another non-issue, but using run doesnt seem to maintain it's environment between subsequent calls in the same test.

e.g.

@test 'something' {
    echo "hello" > "foo/bar"

    run cd foo
    run cat bar
}

the second run will fail because the cd to foo is not maintained

Unable to get --time-limit option to work (via cli or .yml)

If I set the var in the .yml file it causes all of my tests to pass immediately (when they shouldn't) and when I set it on the cli it runs as if no time limit was set. I know it's not working as expected because one of my tests goes into an infinite loop and the zunit process never times out, and must be manually killed.

_zunit_run_testfile:21: tests: assignment to invalid subscript range

$ cat tests/test_cover.zunit
#!/usr/bin/env zunit

@test "cover prints help with -h option" {
  run cover -h
  assert $state equals 0
}
    

$ zunit tests/test_cover.zunit
Launching ZUnit                                            ZUnit: 0.8.2
ZSH:   zsh 5.3.1 (x86_64-debian-linux-gnu)                 
_zunit_run_testfile:21: tests: assignment to invalid subscript range

Feature request: option to copy stderr of run statements to another file / file descriptor

Here is my use-case:

I wrote a shell script to compute code coverage. It uses the xtrace/debug mode of Zsh/Bash to know which lines of which files have been executed.

Initially I wrote it for Bash only, but Bash's LINENO variable is... not always useful. Zsh's LINENO is much better, so I added support for Zsh.

Now, this script will generally be used to run test suites. Typically, with Bats for Bash and ZUnit for Zsh. It works wonderfully with Bats (except for the LINENO detail), because I can use Bash's BASH_XTRACEFD variable which redirect the xtrace output to another file descriptor than stderr.

Unfortunately, such a variable does not exist in Zsh: the xtrace output is always sent to stderr. This is a problem because the xtrace otuput is then captured by Zunit (in run statements), and therefore made unavailable to my coverage script.

This is where my feature request comes to play: while waiting for Zsh to implement such a ZSH_XTRACEFD variable, or similar (I sent a mail to the zsh-workers mailing list asking for it), I would like Zunit to provide an option to copy the stderr of a run command to another file or file descriptor of my choice. Some kind of tee on stderr, something like 2> >(tee >&1 "$zunit_copy_stderr_file").

This example (which is not tested and maybe not working) is explained as:

  • send stderr to a new file descriptor
  • which uses tee to send it back

This feature would allow me to copy the stderr, and therefore xtrace output of Zsh into a file of my choice to further process it later.

Would you be willing to integrate such an option in Zunit? If yes, I can try and open a PR in a few days.

P.S.: I saw earlier that you have a "coverage" branch, but that it was not updated since a relatively long time. It would be great if Zunit had integrated code coverage, but I think an independent script is still a good idea, not being tied to a particular testing framework. This is why I'm opening this ticket 🙂

Allow for specifying a time limit for tests

There are occasions when tests can get stuck in an infinite loop. We should allow for a time limit for tests to be specified.

When the time limit is reached, the subprocess for the test will be killed, and the test marked as errored.

We can also specify an overall time limit for all tests, and cease execution when it is reached.

Warnings should not count as a pass

The only thing that currently generates warnings are risky tests (without any assertions) but when these occur, the test still counts as passed. We should make warnings represent their own state, and not increment the passed count, otherwise the --allow-risky option provides no benefit.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.