WUnit Wollok Unit Testing - uqbar-project/wollok GitHub Wiki
The whole flow of testing begins when a user requests a .wtest
file to be tested:
Before starting
Every time you run a test, a program or REPL console, you are launching a new Wollok VM.
Pros
- You can run any dangerous expression (like a neverending loop) without hanging the original VM. It is a safer approach.
- It is Xtext recommended architecture
Cons
- It is resource and time-consuming (at least 5-6 seconds, every time you want to run the tests)
- It is hard to communicate both VMs. In order to do so, we use a lightweight library called LipeRMI
Let's make the overall picture of the whole process:
First part: launching tests (VM 2)
Before Wollok Launcher starts, we need to setup some things: WollokLauncherParameters object has
- a
tests
flag pointing totrue
value - and a
testPort
number- WollokTestLaunchDelegate sets
testPort
toWollokContextStateNotifier.listeningPort
, from the UI-VM
- WollokTestLaunchDelegate sets
WollokLauncherModule
has sets the test reporter to the corresponding one:
WollokRemoteTestNotifier
if launcher was fired from Wollok IDE (WollokContextStateNotifier
is responsible for finding the first free port in the VM 1)WollokConsoleTestsReporter
if launcher was run from wollok-cli
Second part: Wollok Launcher (VM 2)
Wollok Launcher calls Wollok Interpreter which in turn calls to the WollokInterpreterEvaluator | WollokLauncherInterpreterEvaluator
.
WollokInterpreterEvaluator delegates to several dispatch methods, but one of them is the main for tests/suites: WollokLauncherInterpreterEvaluator.evaluate(WFile)
. If you need to change the way tests are evaluated, this is the right place. Take a look into SuiteBuilder
class.
override dispatch evaluate(WTest test) {
try {
test.elements.forEach [ expr |
interpreter.performOnStack(expr, currentContext) [ | expr.eval ]
]
wollokTestsReporter.reportTestOk(test)
null
} catch (Exception e) {
handleExceptionInTest(e, test)
}
}
protected def WollokObject handleExceptionInTest(Exception e, WTest test) {
if (e.isAssertionException) {
wollokTestsReporter.reportTestAssertError(test, e.generateAssertionError, e.lineNumber, e.URI)
} else {
wollokTestsReporter.reportTestError(test, e, e.lineNumber, e.URI)
}
null
}
- Every time a test passes, test reporter is notified (testOk)
- The same happens if a test has an assertion error or fails
- There are a lot of notifications for the test reporter inside WollokLauncherInterpreterEvaluator
Third part: test reporter notification (VM2 -> VM1)
WollokRemoteTestReporter
collects every test result until the whole testing process finishes:
override finished(long timeElapsedInMilliseconds) {
if (!processingManyFiles) {
remoteTestNotifier.testsResult(testsResult, timeElapsedInMilliseconds)
}
}
remoteTestNotifier
is an instance variable, pointing to WollokRemoteUITestNotifier
interface. The main implementation is in another project: wollok.ui.launch, in the UI VM (we mean, the VM where Wollok IDE is located):
Fourth part: UI WUnit (VM 1)
The main view of WUnit -xUnit implementation- is WollokTestResultView
, an observer of WollokTestResults
(the view model or application model), which has a container of WollokTestResult
. Since RMI calls tend to be expensive, an optimization feature added in Wollok Freire sends the final results in a single message: testResults
. These results are shown in a tree view container implemented by WTestTreeContentProvider
and WTestTreeLabelProvider
. It would be nice to extend this view into a multi-level tree, considering nested folders, describe and tests as possible nodes (nowadays only a single level is available).