PineUnitPineUnit by Guardian667
A comprehensive testing framework for Pine Script on TradingView. Built with well-known testing paradigms like Assertions, Units and Suites. It offers the ability to log test results in TradingView's built-in Pine Protocol view, as well as displaying them in a compact table directly on your chart, ensuring your scripts are both robust and reliable.
Unit testing Pine Script indicators, libraries, and strategies becomes seamless, ensuring the precision and dependability of your TradingView scripts. Beyond standard function testing based on predefined input values, PineUnit supports series value testing. This means a test can run on every bar, taking into account its specific values. Moreover, you can specify the exact conditions under which a test should execute, allowing for series-based testing only on bars fitting a designated scenario.
Detailed Guide & Source Code
Quick Start
To get started swiftly with PineUnit, follow this minimalistic example.
import Guardian667/PineUnit/1 as PineUnit
var testSession = PineUnit.createTestSession()
var trueTest = testSession.createSimpleTest("True is always True")
trueTest.assertTrue(true)
testSession.report()
After running your script, you'll notice a table on your chart displaying the test results. For a detailed log output, you can also utilize the Pine Protocol view in TradingView.
--------------------------------------------------------------
T E S T S
--------------------------------------------------------------
Running Default Unit
Tests run: 1, Failures: 0, Not executed: 0, Skipped: 0
To further illustrate, let's introduce a test that's destined to fail:
var bullTest = testSession.createSeriesTest("It's allways Bull Market")
bullTest.assertTrue(close > open, "Uhoh... it's not always bullish")
After executing, the test results will reflect this intentional discrepancy:
--------------------------------------------------------------
T E S T S
--------------------------------------------------------------
Running Default Unit
Tests run: 2, Failures: 1, Not executed: 0, Skipped: 0 <<< FAILURE! - in
It's allways Bull Market
Uhoh... it's not always bullish ==> expected: , but was
This shows how PineUnit efficiently captures and reports discrepancies in test expectations.
It's important to recognise the difference between `createSimpleTest()` and `createSeriesTest()`. In contrast to a simple test, a series-based test is executed on each bar, making assertions on series values.
License
This source code is subject to the terms of the Mozilla Public License 2.0 at mozilla.org
@ Guardian667
A Personal Note
As a software developer experienced in OO-based languages, diving into Pine Script is a unique journey. While many aspects of it are smooth and efficient, there are also notable gaps, particularly in the realm of testing. We've all been there: using `plotchar()` for debugging, trying to pinpoint those elusive issues in our scripts. I've come to appreciate the value of writing tests, which often obviates the need for such debugging. My hope is that this Testing Framework serves you well and saves you a significant amount of time, more that I invested into developing this "baby."
Unittest
lib_unitLibrary "lib_unit"
functions for assertions and unit testing
method init(this)
Namespace types: Test
Parameters:
this (Test)
method is_true(this, expression, message)
assert that expression is true, if it's false a runtime error will be thrown
Namespace types: Test
Parameters:
this (Test)
expression (bool) : The value to be true
message (string) : The message to print in the runtime error
method is_false(this, expression, message)
assert that expression is false, if it's true a runtime error will be thrown
Namespace types: Test
Parameters:
this (Test)
expression (bool) : The value to be false
message (string) : The message to print in the runtime error
method equals(this, expression, expected, message)
assert if expression and expected are equal, if they don't match a runtime error will be thrown
Namespace types: Test
Parameters:
this (Test)
expression (string) : The value to test
expected (string) : The expected value
message (string) : The message to print in the runtime error
method equals(this, expression, expected, message)
assert if expression and expected are equal, if they don't match a runtime error will be thrown
Namespace types: Test
Parameters:
this (Test)
expression (int) : The value to test
expected (int) : The expected value
message (string) : The message to print in the runtime error
method equals(this, expression, expected, message)
assert if expression and expected are equal, if they don't match a runtime error will be thrown
Namespace types: Test
Parameters:
this (Test)
expression (float) : The value to test
expected (float) : The expected value
message (string) : The message to print in the runtime error
method equals(this, expression, expected, message)
assert if expression and expected are equal, if they don't match a runtime error will be thrown. This version is testing length, order and values
Namespace types: Test
Parameters:
this (Test)
expression (string ) : The array to test
expected (string ) : The expected array
message (string) : The message to print in the runtime error
method equals(this, expression, expected, message)
assert if expression and expected are equal, if they don't match a runtime error will be thrown. This version is testing length, order and values
Namespace types: Test
Parameters:
this (Test)
expression (int ) : The array to test
expected (int ) : The expected array
message (string) : The message to print in the runtime error
method equals(this, expression, expected, message)
assert if expression and expected are equal, if they don't match a runtime error will be thrown. This version is testing length, order and values
Namespace types: Test
Parameters:
this (Test)
expression (float ) : The array to test
expected (float ) : The expected array
message (string) : The message to print in the runtime error
method not_na(this, expression, message)
assert if expression is not na, if it is a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression (string) : The value to test
message (string) : The message to print in the runtime error
method not_na(this, expression, message)
assert if expression is not na, if it is a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression (int) : The value to test
message (string) : The message to print in the runtime error
method not_na(this, expression, message)
assert if expression is not na, if it is a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression (float) : The value to test
message (string) : The message to print in the runtime error
method not_na(this, expression, message)
assert if expression is not na, if it is a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression (string ) : The value to test
message (string) : The message to print in the runtime error
method not_na(this, expression, message)
assert if expression is not na, if it is a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression (int ) : The value to test
message (string) : The message to print in the runtime error
method not_na(this, expression, message)
assert if expression is not na, if it is a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression (float ) : The value to test
message (string) : The message to print in the runtime error
method gt(this, expression1, expression2, message)
assert that expression1 > expression2, if it is not, a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression1 (int) : The value that should be greater
expression2 (int) : The value that should be lesser
message (string) : The message to print in the runtime error
method gt(this, expression1, expression2, message)
assert that expression1 > expression2, if it is not, a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression1 (float) : The value that should be greater
expression2 (int) : The value that should be lesser
message (string) : The message to print in the runtime error
method gte(this, expression1, expression2, message)
assert that expression1 >= expression2, if it is not, a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression1 (int) : The value that should be greater or equal
expression2 (int) : The value that should be lesser or equal
message (string) : The message to print in the runtime error
method gte(this, expression1, expression2, message)
assert that expression1 >= expression2, if it is not, a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression1 (float) : The value that should be greater or equal
expression2 (int) : The value that should be lesser or equal
message (string) : The message to print in the runtime error
method lt(this, expression1, expression2, message)
assert that expression1 < expression2, if it is not, a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression1 (int) : The value that should be lesser
expression2 (int) : The value that should be greater
message (string) : The message to print in the runtime error
method lt(this, expression1, expression2, message)
assert that expression1 < expression2, if it is not, a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression1 (float) : The value that should be lesser
expression2 (int) : The value that should be greater
message (string) : The message to print in the runtime error
method lte(this, expression1, expression2, message)
assert that expression1 <= expression2, if it is not, a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression1 (int) : The value that should be lesser or equal
expression2 (int) : The value that should be greater or equal
message (string) : The message to print in the runtime error
method lte(this, expression1, expression2, message)
assert that expression1 <= expression2, if it is not, a runtime error will be thrown.
Namespace types: Test
Parameters:
this (Test)
expression1 (float) : The value that should be lesser or equal
expression2 (int) : The value that should be greater or equal
message (string) : The message to print in the runtime error
Test
Fields:
strict (series__bool)
verbose (series__bool)
logger (|robbatt/lib_log/2;Logger|#OBJ)
[UTILS] Unit Testing FrameworkTL;DR
This script doesn't provide any buy/sell signals.
This script won't make you profitable implicitly.
This script is intended for utility function testing, library testing, custom assertions.
It is free and open-source.
Introduction
About the idea: is not exclusive, programmers tend to use this method a lot and for a long time.
The point is to ensure that parts of a software, "units" (i.e modules, functions, procedures, class methods etc), work as they should, meet they design and behave as intended. That's why we use the term "Unit testing".
In PineScript we don't have a lot of entities mentioned above yet. What we have are functions. For example, a function that sums numbers should return a number, a particular sum. Or a professor wrote a function that calculates something or whatever. He and you want to be sure that the function works as expected and further code changes (refactoring) won't break its behaviour. What the professor needs to do is to write unit tests for his function/library of functions. And what you need to do is to check if the professor wrote tests or not.
No tests = No code
- Total test-driven development
Ok, it is not so serious, but very important in software development. And I created a tool for that.
I tried to follow the APIs of testing tools/libs/frameworks I worked or work with: Jasmine (Javascript), Mocha/Chai (Javascript), Jest (Javascript), RSpec (Ruby), unittest (Python), pytest (Python) and others. Got something workable but it would be much easier to implement (and it would look much better) if PineScript had a higher-order functions feature.
API
_describe(suiteName: string)
A function to declare a test suite. Each suite with tests may have 2 statuses:
✔️ Passed
❌ Failed
A suite is considered to be failed if at least one of the specs in it has failed.
_it(specName: string, actual: any, expected: any)
A function to run a test. Each test may have 3 statuses:
✔️ Passed
❌ Failed
⛔ Skipped
Some examples:
_it("is a falsey value", 1 != 2, true)
_it("is not a number", na(something), true)
_it("should sum two integers", _sum(1, 2), 1)
_it("arrays are equal", _isEqual(array.from(1, 2), array.from(1, 2)), true)
Remember that both the 'actual' and 'expected' arguments must be of the same type.
And a group of _it() functions must be preceded by a _describe() declaration (see in the code).
_test(specName: string, actual: any, expected: any)
An alias for _it . Does the same thing.
_xit(specName: string, actual: any, expected: any)
A function to skip a particular test for a while. Doesn't make any comparisons, but the test will appear in the results as skipped.
This feature is unstable and may be removed in the future releases.
_xtest(specName: string, actual: any, expected: any)
An alias for _xit . Does the same thing.
_isEqual(id_1: array, id_2: array)
A function to compare two arrays for equality. Both arrays must be of the same type.
This function doesn't take into account the order of elements in each array. So arrays like (1, 2, 3) and (3, 2, 1) will be equal.
_isStrictEqual(id_1: array, id_2: array)
A function to compare two arrays for equality. Both arrays must be of the same type.
This function is a stricter version of _isEqual because it takes into account the order of elements in each array. So arrays like (1, 2, 3) and (3, 2, 1) won't be equal.
Usage
To use this script to test your library you need to do the following steps:
1) Copy all the code you see between line #5 and #282 (Unit Testing Framework Core)
2) Place the copied code at the very beginning of your script (but below study())
3) Start to write suites and tests where your code ends. That's it.
NOTE
The current version is 0.0.1 which means that a lot of things may be changed on the way to 1.0.0 - the first stable version.