Structuring the test suite
====================

Siesta supports writing tests with BDD syntax. We recommend to look at BDD as just a way of structuring 
your tests and not put extra ceremony in it. While it is possible to write your tests in the way,
that test suite will output a readable documentation for your system in the console, it is not the main goal.

Main goal is to write coverage for the features your application have, to sort of "freeze" the spec in
certain state. That means we'll need to write and maintain a lot of tests, so one need to structure them
somehow. We found the following structuring scheme, to suite practical needs very well.


## The tree of sub-tests

A single test file can be organized as several nested sub-tests (in BDD terminology these are called "specs").
Traditionally, {@link Siesta.Test.BDD#describe describe} is used to denote "big", top-level sections:

    StartTest(t => {
    
        t.describe("My system", t => {
            ...
        })
    })

And {@link Siesta.Test#it it} is used to denote tests for smaller, more specific functionality.

    StartTest(t => {
        t.describe("My system", t => {
        
            t.it("Should allow user to log in", t => {
                ...
            })
        })
    })
    
The difference between `describe` and `it` is pure semantically - codewise they are the same, both are just sub tests,
forming a tree, with the top-level test file as the root. Sub tests can be nested arbitrarily, `it` section can contain `describe`
and vice-versa.

Every sub test receives a new {@link Siesta.Test} instance as the 1st argument, which should be used inside the test. 

See also {@link Siesta.Test.getSubTest getSubTest} method.


## Isolation

Best practice will be to make every section standalone, somewhat limited and not depending on others. Then you have a lot of small such sections. 
This makes the test more readable and limits the scope of every testing scenario. 

We found, that readability of tests is optimal, when nesting is not more than 2-3 levels. If you need more levels, then may be its a good idea
to start a new test file instead. You may need to make some functionality re-usable, 
by <a href="#!/guide/extending_test_class">extending the Siesta.Test class</a>.  

You can use {@link Siesta.Test#beforeEach beforeEach}/{@link Siesta.Test#afterEach afterEach} methods to add 
common setup/cleanup code. Overall, usually it looks like this:

    StartTest(t => {
        // global variable accessed by all subtests
        let store
        
        t.beforeEach(function () {
            // do setup, return some fixed dataset for example:
            store = new DataStore({ data : [ { foo : 'bar'} ] })
        })
        
        t.it("Should fire load event", t => {
            store.load()
        
            // `store` instance will be "fresh" not shared with others "it" sections
            t.firesOk(store, "load", 1, "Exactly one load event fired")
            ...
        })
    
        t.it("Should sort correctly", t => {
            // again `store` here will be newly created instance
            store.sort()
            
            // verify sorting
            ...
        })
    })


In the "beforeEach" function you can also for example render some components or do something else (like clear the DB).

Sometimes the "beforeEach" function has to be async:

    StartTest(t => {
        var store
        
        // declare the `beforeEach` with 2 arguments, 2nd is the callback
        t.beforeEach((t, next) => {
            store   = new DataStore({ data : [ { foo : 'bar'} ] })
            
            // call the provided `next` callback when the `beforeEach` action has completed
            // in this case it is asyncronous store loading
            store.load({ callback : next })
        })
        
        t.it("Should do this", t => {
            // `store` instance will be already loaded
            t.expect(store.getCount()).toBeGreaterThan(0)
        })
    
        t.it("Should do that", t => {
            // `store` instance will be already loaded
            ...
        })
    })


## Skipping

You can also easily test just one sub-test by adding the extra letter in the beginning: {@link Siesta.Test#iit iit}, 
{@link Siesta.Test#ddescribe ddescribe}) or exclude some sub-test ({@link Siesta.Test#xit xit}, {@link Siesta.Test#xdescribe xdescribe}).

See also {@link Siesta.Project.failOnExclusiveSpecsWhenAutomated failOnExclusiveSpecsWhenAutomated} method.


## Expectations

Assertions in BDD terminilogy are called *expectations*. 

<p class="side-note">
Siesta supports the expectations syntax as described below. We found it less self-explanatory though, as failed assertions 
lack any context and sometimes its harder to find, what exactly has failed. You can use any of the assertions styles,
or mix both.
</p>  

Expectation can be created with the {@link Siesta.Test.BDD#expect expect} method.

    StartTest(t => {
        t.describe("My system", t => {
            t.it("Should allow user to log in", t => {
                t.expect(MyApp.LoginManager.isLoggedIn()).toBe(false)
                
                MyApp.LoginManager.login()
                
                t.expect(MyApp.LoginManager.isLoggedIn()).toBe(true)
            })
        
            t.describe("Report engine of my system", t => {
                t.it("Should allow generate reports in PDF", t => {
                    ...
                })
            })
        })
    })
   
Please refer to the {@link Siesta.Test.BDD.Expectation} for the list of supported expectations.


## Spies

Siesta implements spies with the same functionality and syntax as seen in Jasmine along with small improvements.
One can spy on a function property of some object or create a standalone spy, suitable for event listener or similar.

    StartTest(t => {
        ...
        
        let spy = t.spyOn(obj, 'process')
        // or, if you need to call the original 'process' method
        let spy = t.spyOn(obj, 'process').and.callThrough()
        
        // call the method
        obj.process('fast', 1)
    
        t.expect(spy).toHaveBeenCalled();
        t.expect(spy).toHaveBeenCalledWith('fast', 1);
    })

Please refer to the {@link Siesta.Test.BDD#spyOn} method and {@link Siesta.Test.BDD.Spy} class.


Buy this product
---------

Visit our store: <https://bryntum.com/store/siesta>


Support
---------

Ask a question in our community forum: <https://www.bryntum.com/forum/viewforum.php?f=20>

Subscribers can post expedited questions in Premium Forum: <https://www.bryntum.com/forum/viewforum.php?f=21>

Please report any bugs through the web interface at <https://www.assembla.com/spaces/bryntum/support/tickets>


See also
---------

Siesta web-page: <https://bryntum.com/products/siesta>

Other Bryntum products: <https://bryntum.com/products>



Attribution
---------

This software contains icons from the following icon packs (licensed under Creative Common 2.5/3.0 Attribution licenses)

- <http://www.famfamfam.com/lab/icons/silk/>
- <http://led24.de/iconset/>
- <http://p.yusukekamiyamane.com/>
- <http://rrze-icon-set.berlios.de/index.html>
- <http://www.smashingmagazine.com/2009/05/20/flavour-extended-the-ultimate-icon-set-for-web-designers/>
- <http://www.doublejdesign.co.uk/products-page/icons/super-mono-icons/>
- <http://pixel-mixer.com/>

Thanks a lot to the authors of the respective icons packs.


COPYRIGHT AND LICENSE
---------

Copyright (c) 2009-2018, Bryntum & Nickolay Platonov

All rights reserved.