Skip to content
This repository has been archived by the owner on Sep 14, 2024. It is now read-only.

Commit

Permalink
Docs website
Browse files Browse the repository at this point in the history
  • Loading branch information
LPGhatguy committed Jun 6, 2018
1 parent 7c3d6ff commit 64aff46
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 192 deletions.
195 changes: 3 additions & 192 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
<a href="https://travis-ci.org/Roblox/testez">
<img src="https://api.travis-ci.org/Roblox/testez.svg?branch=master" alt="Travis-CI Build Status" />
</a>
<a href="https://roblox.github.io/testez">
<img src="https://img.shields.io/badge/docs-website-green.svg" alt="Documentation" />
</a>
</div>

<div align="center">
Expand All @@ -17,198 +20,6 @@ We use TestEZ at Roblox for testing our apps, in-game core scripts, built-in Rob

It provides an API that can run all of your tests with a single method call as well as a more granular API that exposes each step of the pipeline.

## Installation
*In the future, TestEZ will have pre-built model files for use within Roblox without other tools.*

### Method 1: Rojo (Roblox)
* Copy the `lib` directory into your codebase
* Rename the folder to `TestEZ`
* Use [Rojo](https://github.com/LPGhatguy/rojo) to sync the files into a place

### Method 2: Lemur (CI Systems)
You can use [Lemur](https://github.com/LPGhatguy/Lemur) paired together with a regular Lua 5.1 interpreter to run tests written with TestEZ.

This is the best approach when testing Roblox Lua libraries using existing continuous integration systems like Travis-CI. We use this technique to run tests for [Rodux](https://github.com/Roblox/Rodux) and other libraries.

## Creating a Test Script
TestEZ provides two levels of granularity for creating a test script.

The easiest (and recommended) approach is to load the `TestBootstrap` module and `Reporters.TextReporter`. To run all the tests contained in the object `MY_TESTS`, it's just:

```lua
local TestBootstrap = require(TestEZ.TestBootstrap)
local TextReporter = require(TestEZ.Reporters.TextReporter)

TestBootstrap:run(MY_TESTS, TextReporter)
```

The method also returns information about the test run that can be used to take further action.

Alternatively, you can use the other APIs directly. See [the source of `TestBootstrap`](lib/TestBootstrap.lua) as well as [DESIGN.md](DESIGN.md) for details on how to accomplish that.

## Writing Tests
Create `.spec.lua` files (or Roblox objects with the `.spec` suffix) for each module you want to test. These modules should return a function that in turn calls functions from TestEZ.

A simple module and associated TestEZ spec might look like:

`Greeter.lua`
```lua
local Greeter = {}

function Greeter:greet(person)
return "Hello, " .. person
end

return Greeter
```

`Greeter.spec.lua`
```lua
return function()
local Greeter = require(script.Parent.Greeter)

describe("greet", function()
it("should include the customary English greeting", function()
local greeting = Greeter:greet("X")
expect(greeting:match("Hello")).to.be.ok()
end)

it("should include the person being greeted", function()
local greeting = Greeter:greet("Joe")
expect(greeting:match("Joe")).to.be.ok()
end)
end)
end
```

The functions `describe`, `it`, and `expect` are injected by TestEZ and automatically hook into the current testing context.

Every module is implicitly scoped according to its path, meaning the tree that the above test represents might be:

```
LuaChat
Greeter
greet
[+] should include the customary English greeting
[+] should include the person being greeted
```

## Debugging Tests
Often during development, you'll want to only run the test that's concerned with the specific code you're working on.

TestEZ provides the `SKIP()` and `FOCUS()` functions to either skip or focus the block that the call is contained in.

This mechanism does not work for `it` blocks, where you can instead use `itSKIP` and `itFOCUS`. This is because the code inside `it` blocks is not run until the test is executed.

For example, I might want to run the tests targeting a specific method for my `DateTime` module:

`DateTime.spec.lua`
```lua
return function()
describe("ImportantFeature", function()
FOCUS()

it("does really important things", function()
-- This callback *will* run!
end)
end)

describe("Format", function()
it("formats things", function()
-- This callback will never run!
end)
end)
end
```

***`FOCUS` and `SKIP` are intended exclusively for development; future versions of TeztEZ will be able to detect this when running in a CI system and fail tests!***

## TestEZ Test API

### `describe(phrase, callback)`
This function creates a new `describe` block. These blocks correspond to the **things** that are being tested.

Put `it` blocks inside of `describe` blocks to describe what behavior should be correct.

For example:

```lua
describe("This cheese", function()
it("should be moldy", function()
expect(cheese.moldy).to.equal(true)
end)
end)
```

### `it(phrase, callback)`
This function creates a new 'it' block. These blocks correspond to the **behaviors** that should be expected of the thing you're testing.

For example:

```lua
it("should add 1 and 1", function()
expect(1 + 1).to.equal(2)
end)
```

### `FOCUS()`
When called inside a `describe` block, `FOCUS()` marks that block as *focused*. If there are any focused blocks inside your test tree, *only* focused blocks will be executed, and all other tests will be skipped.

When you're writing a new set of tests as part of a larger codebase, use `FOCUS()` while debugging them to reduce the amount of noise you need to scroll through.

For example:

```lua
describe("Secret Feature X", function()
FOCUS()

it("should do something", function()
end)
end)

describe("Secret Feature Y", function()
it("should do nothing", function()
-- This code will not run!
end)
end)
```

**Note: `FOCUS` will not work inside an `it` block. The bodies of these blocks aren't executed until the tests run, which is too late to change the test plan.**

### `SKIP()`
This function works similarly to `FOCUS()`, except instead of marking a block as *focused*, it will mark a block as *skipped*, which stops any of the test assertions in the block from being executed.

**Note: `SKIP` will not work inside an `it` block. The bodies of these blocks aren't executed until the tests run, which is too late to change the test plan.**

### `expect(value)`
Creates a new `Expectation`, used for testing the properties of the given value.

Expectations are intended to be read like English assertions. These are all true:

```lua
-- Equality
expect(1).to.equal(1)
expect(1).never.to.equal(2)

-- Nil checking
expect(1).to.be.ok()
expect(false).to.be.ok()
expect(nil).never.to.be.ok()

-- Type checking
expect(1).to.be.a("number")
expect(newproxy(true)).to.be.a("userdata")

-- Function throwing
expect(function()
error("nope")
end).to.throw()

expect(function()
-- I don't throw!
end).never.to.throw()
```

## Inspiration and Prior Work
The `describe` and `it` syntax in TestEZ is based on the [Behavior-Driven Development](https://en.wikipedia.org/wiki/Behavior-driven_development) methodology, notably as implemented in RSpec (Ruby), busted (Lua), Mocha (JavaScript), and Ginkgo (Go).

Expand Down
115 changes: 115 additions & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
## Inside Tests

### describe
```
describe(phrase, callback)
```

This function creates a new `describe` block. These blocks correspond to the **things** that are being tested.

Put `it` blocks inside of `describe` blocks to describe what behavior should be correct.

For example:

```lua
describe("This cheese", function()
it("should be moldy", function()
expect(cheese.moldy).to.equal(true)
end)
end)
```

### it
```
it(phrase, callback)
```

This function creates a new 'it' block. These blocks correspond to the **behaviors** that should be expected of the thing you're testing.

For example:

```lua
it("should add 1 and 1", function()
expect(1 + 1).to.equal(2)
end)
```

### itFOCUS and itSKIP
```
itFOCUS(phrase, callback)
itSKIP(phrase, callback)
```

These methods are special versions of `it` that automatically mark the `it` block as *focused* or *skipped*. They're necessary because `FOCUS` and `SKIP` can't be called inside `it` blocks!

### FOCUS
```
FOCUS()
```

When called inside a `describe` block, `FOCUS()` marks that block as *focused*. If there are any focused blocks inside your test tree, *only* focused blocks will be executed, and all other tests will be skipped.

When you're writing a new set of tests as part of a larger codebase, use `FOCUS()` while debugging them to reduce the amount of noise you need to scroll through.

For example:

```lua
describe("Secret Feature X", function()
FOCUS()

it("should do something", function()
end)
end)

describe("Secret Feature Y", function()
it("should do nothing", function()
-- This code will not run!
end)
end)
```

!!! note
`FOCUS` does not work inside an `it` block. The bodies of these blocks aren't executed until the tests run, which is too late to change which tests will run.

### SKIP
```
SKIP()
```

This function works similarly to `FOCUS()`, except instead of marking a block as *focused*, it will mark a block as *skipped*, which stops any of the test assertions in the block from being executed.

!!!note
`SKIP` does not work inside an `it` block. The bodies of these blocks aren't executed until the tests run, which is too late to change which tests will run.

### expect
```
expect(value)
```

Creates a new `Expectation`, used for testing the properties of the given value.

Expectations are intended to be read like English assertions. These are all true:

```lua
-- Equality
expect(1).to.equal(1)
expect(1).never.to.equal(2)

-- Nil checking
expect(1).to.be.ok()
expect(false).to.be.ok()
expect(nil).never.to.be.ok()

-- Type checking
expect(1).to.be.a("number")
expect(newproxy(true)).to.be.a("userdata")

-- Function throwing
expect(function()
error("nope")
end).to.throw()

expect(function()
-- I don't throw!
end).never.to.throw()
```
37 changes: 37 additions & 0 deletions docs/getting-started/debugging-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Often during development, you'll want to only run the test that's concerned with the specific code you're working on.

TestEZ provides the `SKIP()` and `FOCUS()` functions to either skip or focus the block that the call is contained in.

This mechanism does not work for `it` blocks; use `itSKIP` and `itFOCUS` instead. Code inside `it` blocks is not run until tests are executed, while `describe` blocks are run immediately to figure out what tests a project contains.

For example, you might want to run the tests targeting a specific method or two for a `DateTime` module:

`DateTime.spec.lua`
```lua
return function()
describe("new", function()
FOCUS()

it("does really important things", function()
-- This block will run!
end)
end)

itFOCUS("has all methods we expect", function()
-- Calling FOCUS() would be too late here, so we use itFOCUS instead.

-- This block will run, too
end)

describe("Format()", function()
it("formats things", function()
-- This block will never run!
end)
end)
end
```

!!! warning
`FOCUS` and `SKIP` are intended exclusively for development. It's not recommended that tests containing these calls are checked into version contorl.

Future versions of TeztEZ will be able to detect this when running in a CI system and fail tests to prevent that from happening.
11 changes: 11 additions & 0 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
*In the future, TestEZ will have pre-built model files for use within Roblox without other tools.*

### Method 1: Rojo (Roblox)
* Copy the `lib` directory into your codebase
* Rename the folder to `TestEZ`
* Use [Rojo](https://github.com/LPGhatguy/rojo) to sync the files into a place

### Method 2: Lemur (CI Systems)
You can use [Lemur](https://github.com/LPGhatguy/Lemur) paired together with a regular Lua 5.1 interpreter to run tests written with TestEZ.

This is the best approach when testing Roblox Lua libraries using existing continuous integration systems like Travis-CI. We use this technique to run tests for [Rodux](https://github.com/Roblox/Rodux) and other libraries.
11 changes: 11 additions & 0 deletions docs/getting-started/running-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
TestEZ provides a convenient method to run tests in a single pass:

```lua
local TestEZ = require(<path to TestEZ>)

TestEZ.TestBootstrap:run(MY_TESTS)
```

The method also returns information about the test run that can be used to take further action!

The internals of TestEZ are being reworked, so accessing other APIs at this time isn't recommended.
Loading

0 comments on commit 64aff46

Please sign in to comment.