Redux Saga: Throw and stop generator - unit-testing

I'm writing a generator. I'm testing it with RITEway. It checks if window.ethereum is defined. If its not, it should throw and stop. Basically it should satisfy the following tests:
describe('handle initialize Web3 saga', async assert => {
global.window = {}
assert({
given: 'nothing, the window object',
should: 'have no property called Web3',
actual: window.web3,
expected: undefined
})
const gen = cloneableGenerator(handleInitializeWeb3)()
{
// The important parts are in this block scope
const clone = gen.clone()
assert({
given: 'window.ethereum undefined',
should: 'throw',
actual: clone.next().value.message,
expected: '[WARNING]: window.ethereum has no provider!'
})
assert({
given: 'nothing',
should: 'be done',
actual: clone.next().done,
expected: true
})
}
class Provider {}
window.ethereum = new Provider()
// ... more tests
})
Here is how I tried implementing it.
function* handleInitializeWeb3() {
if (!window.ethereum) {
yield new Error('[WARNING]: window.ethereum has no provider!')
}
// ... more yields
}
but this saga doesn't stop. The test where it should: 'be done' fails and the saga gives back the values from the yields outside of the if statement. How can I have these tests pass and the saga stop when the error is thrown?

yielding an error instance acts the same as yielding any other value (i.e. the generator keeps running). If you want to stop the generator you should throw new Error(... like in a normal function.
If for some reason you don't want to throw and do in fact want to yield an error instance and then stop, simply return; after you've yielded the error.

Related

Ember Observers getting broken

I have a complex app and we do write test cases for testing.
While writing a test case for one case, we came to know about this issue.
I'm having a file in which two acceptance tests are written, something like this:
module() {
test('acceptance 1', function() {
...logic
}
test('acceptance 2', function() {
...logic
}
}
As we all know, whenever a property changes both beginPropertyChanges and endPropertyChanges function will be called. What happens in my case is, when the acceptance 1 test is running it increments the deferred variable in beginPropertyChanges and due to some error endPropertyChanges haven't triggered. Due to this all observers are getting broken. (As deferred variable will be 1 and not zero)
function beginPropertyChanges() {
deferred++;
suspendedObserverDeactivation();
}
function endPropertyChanges() {
deferred--;
if (deferred <= 0) {
flushSyncObservers();
resumeObserverDeactivation();
}
}
So when acceptance 2 test run, it fails due to observers brokage.
How I can fix this (how to make deferred as zero when test 2 is running) so that test 2 will get passed?

Mock Ktor's http client with MockK

I want to mock requests with ktor's http client using MockK. The problem is all the methods related to making requests with the client are inline, so I cannot use coEvery on those methods. The next thing I tried was to go through the called methods until I found a method that wasn't inline and then mock that. After stepping through some functions, the HttpClient.request() function instantiates an HttpStatement and then calls execute() on it.
public suspend inline fun HttpClient.request(
builder: HttpRequestBuilder = HttpRequestBuilder()
): HttpResponse = HttpStatement(builder, this).execute()
If I can mock the constructor and .execute() functions, I can intercept the call and return my canned response. I can then check that the builder's params are correct inside of a verify function.
mockkConstructor(HttpStatement::class)
coEvery { anyConstructed<HttpStatement>().execute() } returns mockk {
coEvery { status } returns HttpStatusCode.OK
coEvery { body<RefreshToken>() } returns RefreshToken()
}
This code takes care of intercepting the execute call. The next step would be to verify the constructor params of HttpStatement. This code to verify execute was called works:
coVerify { anyConstructed<HttpStatement>().execute() }
Next thing is to verify the constructor params. This pull request in the MockK repo describes how to verify constructors:
coVerify { constructedWith<HttpStatement>(/* Matchers here */).execute() }
Note that I have to add the .execute() or else MockK tells me I'm not verifying anything.
Missing calls inside verify { ... } block.
io.mockk.MockKException: Missing calls inside verify { ... } block.
at app//io.mockk.impl.recording.states.VerifyingState.checkMissingCalls(VerifyingState.kt:52)
at app//io.mockk.impl.recording.states.VerifyingState.recordingDone(VerifyingState.kt:21)
...
Ok, so just add in the matchers. However, no combination of matchers I try works. I've tried doing a bunch of constant matchers for type Any (which should match anything right?)
coVerify { constructedWith<HttpStatement>(ConstantMatcher<Any>(true))}
I've tried a matcher for HttpRequestBuilder and HttpClient
coVerify {
constructedWith<HttpStatement>(
ConstantMatcher<HttpRequestBuilder>(true),
ConstantMatcher<HttpClient>(true)
).execute()
}
And a whole slew of others. Each time, I get this error:
Verification failed: call 1 of 1: HttpStatement(mockkConstructor<HttpStatement>(any(), any())).execute(any())) was not called
java.lang.AssertionError: Verification failed: call 1 of 1: HttpStatement(mockkConstructor<HttpStatement>(any(), any())).execute(any())) was not called
at io.mockk.impl.recording.states.VerifyingState.failIfNotPassed(VerifyingState.kt:63)
at io.mockk.impl.recording.states.VerifyingState.recordingDone(VerifyingState.kt:42)
...
Next thing I figured I could try would be to use an answers block earlier on in order to print out the types of the parameters being passed in case I was wrong, but that also runs into the "nothing being done in every block" error.
coEvery { anyConstructed<HttpStatement>() } answers {
args.filterNotNull().map { it::class.qualifiedName }.forEach(::println)
mockk {
coEvery { execute().status } returns HttpStatusCode.OK
coEvery { execute().body<RefreshToken>() } returns RefreshToken(
accessToken = accessToken,
expiresIn = expiresIn,
)
}
}
Is there a solution to mocking the http client? Do I have to mock something even more internal? Or do I just have to stick to using the ktor MockEngine?

toHaveBeenCalled() failing when mocked output shows correct data

I am getting tired of trying to figure out the following out. Basically I have a method in my component that calls a service which is mocked. Once that service completes, another service does some logging activities, which is also mocked. But my test fails saying the logging service wasn't called:
process(){
const that : any = this;
this.mainService.process().then(result=>{
return result;
}).then(result=>{
//log the operation now after doing some checkups
let checkups = ""
that.logService.log('process',result, checkups).then(logged=>{
console.log(logged)
}).catch(err=>console.log(err)
}).catch(err=>console.log(err);
}
Before we go ahead, doing Promise.all() isn't match of an option due to the logic in place to do checkups. Now to the testing bit:
fit("should log processed request", done => {
const mainSerivce = TestBed.get(MainService)
const logService = TestBed.get(LogService)
spyOn(mainService, "process").and.returnValue(Promise.resolve({id:34,value:64, rank:310));
const logSpy = spyOn(logSerivce, "log").and.returnValue(Promise.resolve({'done':true}))
fixture.whenStable().then(finished=>{
component.process();
expect(logSpy).toHaveBeenCalled();
done();
})
});
expect(logSpy).toHaveBeenCalled();
fails now but I can see in my console the result of the mock {'done':true} or whatever value I pass is shown, meaning it was mocked and called (?). What am I missing exactly since the methods appear to have been mocked and logged correctly in the console.
It seems to me you have to wait for the promises to resolve before asserting for it, try:
fit("should log processed request", done => {
const mainSerivce = TestBed.get(MainService)
const logService = TestBed.get(LogService)
spyOn(mainService, "process").and.returnValue(Promise.resolve({id:34,value:64, rank:310));
const logSpy = spyOn(logSerivce, "log").and.returnValue(Promise.resolve({'done':true}));
// call the function that will resolve promises
component.process();
// whenStable waits for the promises to resolve.
fixture.whenStable().then(finished=>{
console.log('Making the assertion !!');
expect(logSpy).toHaveBeenCalled();
done();
});
});
Make sure you see the log of { 'done': true } before the log of Making the assertion !!. But since you have a promise resolving within a promise, the following might fix it.
fit("should log processed request", async done => { // check out the async keyword here
const mainSerivce = TestBed.get(MainService)
const logService = TestBed.get(LogService)
spyOn(mainService, "process").and.returnValue(Promise.resolve({id:34,value:64, rank:310));
const logSpy = spyOn(logSerivce, "log").and.returnValue(Promise.resolve({'done':true}));
// call the function that will resolve promises
component.process();
// when stable waits for the promises to resolve.
await fixture.whenStable();
await fixture.whenStable();
expect(logSpy).toHaveBeenCalled();
done();
});

Jest coverage testing global objects and inverse condition

I've got a global object on window that has a function. In my code I'm writing this:
if (window.foo) {
window.foo.bar();
}
In the tests, when window.foo has a value I assert that window.foo.bar has been called. Easy enough.
Jest coverage is complaining that I'm not testing the negative value, i.e. when window.foo is undefined. But I've been struggling to work out what to assert on.
What I'd like to do - is mock window.foo and assert that it is only called once, when we check whether it has a value or is undefined (i.e. the call to window.foo.bar is never made.
I'm trying to mock the global window object and return an object but I'm getting confused as to how to mock and spyOn a value when it isn't a function, and then check it has been accessed.
Any help appreciated!
You could use a getter so whenever a property in the object is being accessed inside the getter we could run multiple actions, in our case we just trigger our spy manually.
describe('window.foo', () => {
afterEach(() => {
delete global.foo // make sure you clean your global object after each test
})
it('should be called twice', () => {
const fooSpy = jest.fn();
const barSpy = jest.fn();
Object.defineProperty(global, 'foo', {
configurable: true, // very important or else you can't delete
get() {
fooSpy(); //we manually call our spy so we can assert later
// and we return an object with another spy for our bar function
return { bar: barSpy};
}
});
if (global.foo) {
global.foo.bar()
}
expect(fooSpy).toHaveBeenCalledTimes(2);
expect(barSpy).toHaveBeenCalledTimes(1);
});
it('should be called once', () => {
const fooSpy = jest.fn();
Object.defineProperty(global, 'foo', {
writconfigurableable: true,
get() {
fooSpy(); // we trigger the spy manually
// we return undefined
return undefined;
}
});
if (global.foo) {
global.foo.bar()
}
expect(fooSpy).toHaveBeenCalledTimes(1);
});
});
You can see it working in here

NSubstitute conditions for throwing exception other than parameters

I'm using NSubstitute to mock a class that my method under test uses. This mocked class may throw a particular exception under certain conditions.
The method that I'm testing has some "retry" logic that it executes when it catches this exception. I'm trying to test this retry logic. So, I need a particular method of this mocked class to throw the exception sometimes, but not other times. Unfortunately, the method that throws this exception has no parameters, so I can't base the throw logic on parameters.
How can I make the mocked object's method throw the exception either:
A) ...the first N times it's called
or
B) ...based on the parameters some other method that's called before it
or
C) ...under any other condition other than the parameters passed in
To give you a clearer picture of what I'm trying to do, my code is something like:
IDataSender myDataSender = GetDataSender();
int ID = GetNextAvailableID();
myDataSender.ClearData();
myDataSender.Add(ID,"DataToSend");
bool sendSuccess = false;
while (!sendSuccess)
{
try
{
myDataSender.SendData();
sendSuccess = true;
}
catch (IDCollisionException)
{
ID++;
MyDataSender.ClearData();
myDataSender.Add(ID,"DataToSend");
}
}
So, I need to test my retry logic, and I need to simulate that IDCollisionException. However, I can't have the SendData() throwing the exception every single time, or the retry loop will never succeed.
What can I do here?
If I understand the question correctly, you can use When..Do and close over a local variable to get this behaviour.
const int throwUntil = 3;
var callsToSendData = 0;
var dataSender = Substitute.For<IDataSender>();
dataSender
.When(x => x.SendData())
.Do(x =>
{
callsToSendData++;
if (callsToSendData < throwUntil)
{
throw new DbCollisionException();
}
});
Similarly, you can also use callbacks to locally capture parameters passed to other methods, and access them within the Do block (rather than just using a counter).