diff --git a/_en/quality/luacheck.md b/_en/quality/luacheck.md index fee2e5b..a734b61 100644 --- a/_en/quality/luacheck.md +++ b/_en/quality/luacheck.md @@ -18,7 +18,7 @@ editor to provide alerts to any mistakes. * [Running LuaCheck](#running-luacheck) * [Configuring LuaCheck](#configuring-luacheck) * [Troubleshooting](#troubleshooting) -* [Checking commits with Travis](#checking-commits-with-travis) +* [Checking Commits with Travis](#checking-commits-with-travis) ## Installing LuaCheck @@ -103,7 +103,7 @@ available. * **Atom** - `linter-luacheck` * **Sublime** - `SublimeLinter-luacheck` -## Checking commits with Travis +## Checking Commits with Travis If your project is public and is on Github, you can use TravisCI - a free service to run jobs on commits to check them. This means that every commit you push will diff --git a/_en/quality/unit_testing.md b/_en/quality/unit_testing.md index 46e5d4e..b8006eb 100644 --- a/_en/quality/unit_testing.md +++ b/_en/quality/unit_testing.md @@ -7,34 +7,185 @@ idx: 6.4 ## Introduction -Unit tests are an essential tool in proving and resuring yourself that your code +Unit tests are an essential tool in proving and reassuring yourself that your code is correct. This chapter will show you how to write tests for Minetest mods and -games using busted, and how to structure your code to make this easier - Writing -unit tests for functions where you call Minetest functions is quite difficult, -but luckily [in the previous chapter](mvc.html), we discussed how to make your -code avoid this. +games using busted. Writing unit tests for functions where you call Minetest +functions is quite difficult, but luckily [in the previous chapter](clean_arch.html) + we discussed how to make your code avoid this. * [Installing Busted](#installing-busted) - * [Windows](#windows) - * [Linux](#linux) - +* [Your First Test](#your-first-test) +* [Mocking: Using External Functions](#mocking-using-external-functions) +* [Checking Commits with Travis](#checking-commits-with-travis) +* [Conclusion](#conclusion) ## Installing Busted -### Windows +First you'll need to install LuaRocks. -*Todo. No one cares about windows, right?* +* Windows: Follow the [installation instructions on LuaRock's wiki](https://github.com/luarocks/luarocks/wiki/Installation-instructions-for-Windows). +* Debian/Ubuntu Linux: `sudo apt install luarocks` -### Linux - -First you'll need to install LuaRocks: - - sudo apt install luarocks - -You can then install Busted globally: +Next you should then install Busted globally: sudo luarocks install busted -Check that it's installed with the following command: +Finally, check that it is installed: busted --version + + +## Your First Test + +Busted is Lua's leading unit test framework. Busted looks for Lua files with +names ending in `_spec`, and then executes them in a standalone Lua environment. + + mymod/ + ├── init.lua + ├── api.lua + └── tests + └── api_spec.lua + + +### init.lua + +{% highlight lua %} +mymod = {} + +dofile(minetest.get_modpath("mymod") .. "/api.lua") +{% endhighlight %} + + + +### api.lua + +{% highlight lua %} +function mymod.add(x, y) + return x + y +end +{% endhighlight %} + +### tests/api_spec.lua + +{% highlight lua %} +-- Look for required things in +package.path = "../?.lua;" .. package.path + +-- Set mymod global for API to write into +_G.mymod = {} --_ +-- Run api.lua file +require("api") + +-- Tests +describe("add", function() + it("adds", function() + assert.equals(2, mymod.add(1, 1)) + end) + + it("supports negatives", function() + assert.equals(0, mymod.add(-1, 1)) + assert.equals(-2, mymod.add(-1, -1)) + end) +end) +{% endhighlight %} + +You can now run the tests by opening a terminal in the mod's directory and +running `busted .` + +It's important that the api file doesn't create the table itself, as globals in +Busted work differently. Any variable which would be global in Minetest is instead +a file local in busted. This would have been a better way for Minetest to do things, +but it's too late for that now. + +Another thing to note is that any files you're testing should avoid calls to any +functions not inside of it. You tend to only write tests for a single file at once. + + +## Mocking: Using External Functions + +Mocking is the practice of replacing functions that the thing you're testing depends +on. This can have two purposes - firstly, the function may not be available in the +test environment. Secondly, you may want to capture calls to the function and any +passed arguments. + +If you follow the advice in the [Clean Architectures](clean_arch.html) chapter, +you'll already have a pretty clean file to test. You will still have to mock +things not in your area however - for example, you'll have to mock the view when +testing the controller/API. If you didn't follow the advice, then things are a +little harder as you may have to mock the Minetest API. + +{% highlight lua %} +-- As above, make a table +_G.minetest = {} + +-- Define the mock function +local chat_send_all_calls = {} +function minetest.chat_send_all(name, message) + table.insert(chat_send_all_calls, { name = name, message = message }) +end + +-- Tests +describe("list_areas", function() + it("returns a line for each area", function() + chat_send_all_calls = {} -- reset table + + mymod.list_areas_to_chat("singleplayer", "singleplayer") + + assert.equals(2, #chat_send_all_calls) + end) + + it("sends to right player", function() + chat_send_all_calls = {} -- reset table + + mymod.list_areas_to_chat("singleplayer", "singleplayer") + + for _, call in pairs(chat_send_all_calls) do --_ + assert.equals("singleplayer", call.name) + end + end) + + -- The above two tests are actually pointless, as this one tests both things + it("returns correct thing", function() + chat_send_all_calls = {} -- reset table + + mymod.list_areas_to_chat("singleplayer", "singleplayer") + + local expected = { + { name = "singleplayer", message = "Town Hall (2,43,63)" }, + { name = "singleplayer", message = "Airport (43,45,63)" }, + } + assert.same(expected, chat_send_all_calls) + end) +end) +{% endhighlight %} + + +## Checking Commits with Travis + +The Travis script from the [Error Checking](luacheck.html) +chapter can be modified to also run Busted. + +{% highlight yml %} +language: generic +sudo: false +addons: + apt: + packages: + - luarocks +before_install: + - luarocks install --local luacheck && luarocks install --local busted +script: +- $HOME/.luarocks/bin/luacheck --no-color . +- $HOME/.luarocks/bin/busted . +notifications: + email: false +{% endhighlight %} + + +## Conclusion + +Unit tests will greatly increase the quality and reliability of your project if used +well, but they require you to structure your code in a different way than usual. + +For an example of a mod with lots of unit tests, see +[crafting by rubenwardy](https://github.com/rubenwardy/crafting).