10 spies - ranhs/soda-test GitHub Wiki

Sample code

The following function (in demo.ts) do some operation and then log a line to console, and also a warnning. In this section we want to validate the log to the console.

export function foo() {
    // some operation
    console.log('console.log was called')
    console.warn('console.warn was called')

    return
}

Spy method on object in a test-step

In this example we want to call the foo method, and validate the call to console.log. Note that log is a method defined in the console object. We need to create a "spy" on this method.
In the test-step method, define an argument of type SinonSpy. To make this argument become a spy object, we need to add decorator to the argument called spy and give to the decorator 2 arguments: the object the method to spy is on it, and the name of the method to spy

@it('should spy on log')
logSpy( @spy(console, 'log') console_log_spy: SinonSpy ): TR {
    foo()
    expect(console_log_spy.calledOnce).to.be.true
    expect(console_log_spy).to.have.been.calledOnce
}

After calling log method we can check the spy object to validate it was called. we can do this by using the calledOnce property on the spy, or better to use the sinon_chai plugin already include in soda-test. You can use expect on the spy itself, and expect the spy to.have.been.calledOnce or to.have.been.calledWith and check the argument to the spy method.

Spy in class or context level

You can define a spy for several test-steps, by defining the SinonSpy not as an argument but as a member variable, and using the spy decorator on it in the same way. If you define the spy member on the global section it will be available in all test-steps in the class. If you define it inside a specific context, it will be available in each test-step of that context, but not in test-steps of other contexts. Note that in each test-step the spy is resetted, so you can check for the calls on the spy method in the current test-step.

@spy(console, 'log')
console_log_spy: SinonSpy

@it("should spy as member")
logSpy(): TR {
    foo()
    expect(this.console_log_spy.calledOnce).to.be.true
    expect(this.console_log_spy).to.have.been.calledOnce
}

Spy Method in a Libraray

In the above example, the method to spy was defined on an object that you can access. Sometime you need to spy a method that define in a libraray (of current project, or referenced libraray). You can import the whole libraray and use it and the target object to spy, but this is not recommended, and some cases will not work.
To define a spy on a method in a libraray you pass to the spy decorator 2 arguments: the name of the libraray (as used in import statement, and the name of the method.

@it("should spy external library as argument")
checkSpy3(@spy('lodash', 'isString') spyIsString: SinonSpy): TR {
    let b = isString('AAA')
    expect(b).to.be.true
    expect(spyIsString).to.have.been.calledOnce
}

You can use this sentax also of internal libraraies

@it("should spy as argument")
checkSpy4(@spy('./lib', 'advance') spyAdvance: SinonSpy): TR {
    advance()
    expect(get_count()).to.equal(19)
    expect(spyAdvance).to.have.been.calledOnce
}

Incase of internal library, you can use this syntax to spy private method (methods that are not exported)
You can use both syntaxes to define a spy as an arugment to a test-step, or to define a spy to context/class as member variable.

Spy the main library Method

If the method you need to spy is the libraray itself or the default method of the library, you can use the spy decorator with 1 argument only: the name of the libraray.

import * as parseFunction from 'parse-function'
// or
// import parseFunction from 'parse-function'

@it("should spy the library main method")
heckSpy5(@spy('parse-function') parseFuncSpy: SinonSpy): TR {
   parseFunction()
   expect(parseFuncSpy).to.have.been.calledOnce
}

Spy on Class Member Method

If the method you need to spy on is not a method on the libraray, but rather a member method in a class in a libaray, you can use the spy decorator with 3 arguments: the name of the libraray, the name of the class, the name of the class member method.

@it("should spy class member")
heckSpy6(@spy('./lib', 'DummyClass', 'kuku') kukuSpy: SinonSpy): TR {
    let dummy = new DummyClass()
    let n = dummy.foo('stam')
    expect(kukuSpy).to.have.been.calledOnce
    expect(kukuSpy).to.have.been.calledWith('stam')
    expect(n).to.equal(4)
}

Note that this syntax can also be used in the context/class level
Note: if you need to spy on the class constrator, just put the name of the libraray and the name of the class, since class constractor is just a function. (*this might not always work)

⚠️ **GitHub.com Fallback** ⚠️