Not sure if it's a common norm in unit testing nomenclature, but I've been writing some of my Jest unit tests like the following, and it just doesn't feel right to group the expected output with the given input(s) as fixture:
describe('24-25', () => {
test('test 24', () => {
// given
const fixture = {
givenInput: 24,
expectedOutput: 50,
};
// when, then
testDoubleInc(fixture);
});
test('test 25', () => {
// given
const fixture = {
givenInput: 25,
expectedOutput: 52,
};
// when, then
testDoubleInc(fixture);
});
function testDoubleInc({ givenInput, expectedOutput }) {
expect(doubleInc(givenInput)).toEqual(expectedOutput);
}
});
// Unit Under Test
function doubleInc(x) {
return (x + 1) * 2;
}
So the question is, have I been misusing the term "fixture" there? And if so, would it be more precise/correct in writing it this way instead?
// given
const fixture = {
givenInput: 24,
};
const expectedOutput = 50;
// when, then
testDoubleInc({ givenInput: fixture.givenInput, expectedOutput });
Related
how to write unit test under this situation?
Vue Documents like this:
function alwaysEnabled() {
return false;
}
const actionDisabledFunctions = {
[CASE_ACTIONS.FLAG_CASE]: alwaysEnabled,
};
how do I write unit test inside describe?
describe('actionDisabledFunctions[CASE_ACTIONS.FLAG_CASE]', () => {
it('', () => {
});
});
After much trial/error, searching here on SO, & flexing my Google Fu, throwing in the towel & asking for help.
TL/DR -
Trying to correctly mock node module, change internal method return types, and spy on ctor's & method calls within the node module.
My specific scenario is to test the Microsoft Azure Storage blob SDK #azure/storage-blob, but the questions aren't specific to this package. It's just a good example as 4 LOC's capture achieve a task (upload a file to a storage container) as 2-3 of those LOC's cover 4 scenarios. Here's my code that I want to test, with comments on WHAT exactly I want to test:
export async function saveImage(account: string, container: string, imageBuffer: Buffer, imageName: string): Promise<void> {
try {
// init storage client
// (1) want to spy on args passed into ctor
const blobServiceClient: BlobServiceClient = new BlobServiceClient(`https://${account}.blob.core.windows.net`, new DefaultAzureCredential());
// init container
// (2) same as #1
const containerClient: ContainerClient = await blobServiceClient.getContainerClient(container);
// init block blob client
// (3) same as #1 & #2
const blockBlobClient: BlockBlobClient = containerClient.getBlockBlobClient(imageName);
// save file
// (4) same as #1,2, & 3
// (5) manipulate returned value
// (6) throw cause method to internally throw error
await blockBlobClient.upload(imageBuffer, imageBuffer.length, { blobHTTPHeaders: { blobContentType: 'image/png' } });
return Promise.resolve();
} catch (err: Error) {
return Promise.reject(err);
}
}
I've setup a manual mock for the module in the ./__mocks/#azure/storage-blob.ts as follows:
const MockStorageBlob = jest.createMockFromModule('#azure/storage-blob');
/**
* Utility method throw exception in the `BlockBlobClient.upload()` method.
*/
(MockStorageBlob as any).__setBlockBlobUpload_toFail = () => {
(MockStorageBlob as any).BlobServiceClient = jest.fn().mockImplementation(() => {
return {
getContainerClient: jest.fn().mockReturnValue({
getBlockBlobClient: jest.fn().mockReturnValue({
upload: jest.fn().mockImplementation(() => {
throw new Error('synthetic error');
})
})
})
}
});
}
module.exports = MockStorageBlob;
In my test, I can successfully test for #6 above like this:
import {
BlockBlobClient,
BlockBlobUploadResponse
} from '#azure/storage-blob';
import { saveImageToCDN as functionUnderTest } from './saveImageToCDN';
// mock Azure Storage blob NPM package
jest.mock('#azure/storage-blob');
describe('check expected with failure', () => {
beforeEach((done) => {
// reset number of times things have been called
jest.clearAllMocks();
done();
});
test(`it calls 'trackException()' when upload throws exception`, async (done) => {
expect.assertions(1);
// setup test
require('#azure/storage-blob').__setBlockBlobUpload_toFail();
// run SUT
const imageBuffer = Buffer.from('test string');
functionUnderTest(imageBuffer, 'imageName.png')
.then(() => {
expect(new Error('should not reach this')).toBeUndefined();
})
.catch((err: Error) => {
expect(err).toBeDefined();
})
.finally(() => {
done();
});
});
});
... but I can't figure out the correct syntax to spy on the upload() method (#4), or any of the other things I'm trying to test for (#1-5). If it matters, using Jest v26 on Node v14.
Could the __setBlockBlobUpload_toFail function return references to the mock functions ?
That would give something like this :
const MockStorageBlob = jest.createMockFromModule('#azure/storage-blob');
/**
* Utility method throw exception in the `BlockBlobClient.upload()` method.
*/
(MockStorageBlob as any).__setBlockBlobUpload_toFail = () => {
const upload = jest.fn().mockImplementation(() => {
throw new Error('synthetic error');
});
const getBlockBlobClient = jest.fn().mockReturnValue({ upload });
const getContainerClient = jest.fn().mockReturnValue({ getBlockBlobClient });
const BlobServiceClient = jest.fn().mockImplementation(() => {
return {
getContainerClient
}
});
(MockStorageBlob as any).BlobServiceClient = BlobServiceClient;
return {
upload,
getBlockBlobClient,
getContainerClient,
BlobServiceClient
};
}
module.exports = MockStorageBlob;
And in your test you would retrieve them like :
// setup test
const mockFns = require('#azure/storage-blob').__setBlockBlobUpload_toFail();
// run SUT
const imageBuffer = Buffer.from('test string');
functionUnderTest(imageBuffer, 'imageName.png')
.then(() => {
expect(new Error('should not reach this')).toBeUndefined();
})
.catch((err: Error) => {
expect(mockFns.getBlockBlobClient.mock.calls[0][0]).toBe('imageName.png')
expect(err).toBeDefined();
})
.finally(() => {
done();
});
I have been attempting to write an RxJS marble test for a simple redux-observable epic but cannot get it to pass. It appears that using fromPromise in the observable chain under test does not emit the items per the expected marble sequence when flush is called on the testScheduler.
Providing a sample of the test. If I replace Observable.fromPromise to Observable.of the test will pass.
Any insight is appreciated. RxJS 5 / redux-observable 0.18
...
const MY_ACTION = 'MY_ACTION';
const myAction = () => ({
type: MY_ACTION,
payload: {test: 'testval'},
});
const epic = action$ =>
action$.ofType(MY_ACTION).switchMap(() =>
Observable.concat(
Observable.of({type: 'test1'}),
Observable.fromPromise(Promise.resolve({type: 'test2'})),
)
);
it('it should work', () => {
const deepEquals = (actual, expected) => {
expect(actual).to.deep.equal(expected);
};
const createTestScheduler = () =>
new TestScheduler(deepEquals);
const marbles1 = '-a-';
const marbles2 = '-(bc)-';
const values = {
a: myAction(),
b: {type: 'test1'},
c: {type: 'test2'},
};
const ts = createTestScheduler();
const source = ActionsObservable.from(
ts.createColdObservable(marbles1, values)
);
const actual = epic(source);
ts.expectObservable(actual);
ts.expectObservable(actual).toBe(marbles2, values);
ts.flush();
});
...
Take a look at the documentation under the 'Known issues' section: https://rxjs-dev.firebaseapp.com/guide/testing/marble-testing
You can use the from operator to wrap a promise like so:
const myAsyncCode = () => from(Promise.resolve('something'));
it('has async code', (done) => {
myAsyncCode().subscribe((d) => {
assertEqual(d, 'something');
done();
});
});
I want to start writing unit tests for my code. I thought I'd start with a nice and simple function.
string => string
in action here: http://jsbin.com/yufuzawalo/edit?js,console,output
const animEndString = (type = 'transition') => {
let types =
type === 'transition'
? {
OTransition: 'oTransitionEnd',
WebkitTransition: 'webkitTransitionEnd',
MozTransition: 'transitionend',
transition: 'transitionend'
}
: {
OAnimation: 'oAnimationEnd',
WebkitAnimation: 'webkitAnimationEnd',
MozAnimation: 'animationend',
animation: 'animationend'
}
const elem = document.createElement('div')
return Object.keys(types).reduce(function(prev, trans) {
return undefined !== elem.style[trans] ? types[trans] : prev
}, '')
}
And the test:
describe('example', () => {
it('should return animationend when passed the string animation', () => {
const value = animationEnd('animation')
expect(value).toBe('animationend')
})
it('should return transitionEnd when passed the string animation', () => {
const value = animationEnd('transition')
expect(value).toBe('transitionend')
})
})
output:
example › should return transitionEnd when passed the string animation
expect(received).toBe(expected) // Object.is equality
Expected value to be:
"transitionend"
Received:
The test is failing as an empty string is being returned. I'm presuming that Jest doesn't know what to do with document.createElement('fake')
How would I get around this issue?
Solved...
I've added the following to my setup-jest file
global.document.createElement = jest.fn(() => ({
style: {
transition: 'opacity 300ms ease',
animation: 'test 300ms'
}
}))
I am working on a React Native component handling gestures via PanResponder.
I would like to test that when certain dx, dy values are passed to the onPanResponderRelease method, expected action will be executed. Please find below an example of what I would like to test:
export default class MyComponent extends Component<Props, State> {
constructor(props: Props) {
super(props);
this._panResponder = this._initPanResponder();
}
_initPanResponder(): PanResponder {
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onPanResponderMove: Animated.event([
null,
{ dx: this._animation.x, dy: this._animation.y }
]),
onPanResponderRelease: (e, gestureState) => {
const { dx, dy } = gestureState;
if (dy > 100) {
doSomething(); // <---- what I would like to unit test
}
}
});
return panResponder;
}
}
Is there any straightforward way to unit test the following with Jest?
I tend to leave testing the wiring code out, since testing an actual PanResponder involves a UI test that is hard to write, is usually flaky, and gives little value.
What you can do, is extract the event handling function out, and test it independently.
In that case, the test would very simple, as all you need to do is invoke the handler in the test and see you're getting what you expected.
If you want to test user's interection with the component you should mock PanResponder in a way you forward the functions given as params to PanResponder.create to the panHandlers it returns.
// setup-tests.ts
jest.doMock('react-native', () => {
return Object.setPrototypeOf(
PanResponder: {
...ReactNative.PanResponder,
create: (config: any) => ({ panHandlers: config }),
},
},
ReactNative
);
});
Tha way, when yoou spread panHandlers in the View you are handling Pan, you can access the functions the way you set up in the file
// pan-handling-component.tsx
const Component: React.FC = () => {
...
const panResponder = React.useRef(
PanResponder.create({
...
onPanResponderMove: (_, gestureState) => {
Animated.event([slideAnim], { useNativeDriver: false })(
gestureState.moveY
);
},
onPanResponderTerminationRequest: () => true,
onPanResponderRelease: () => {
// #ts-ignore
const currentValue = slideAnim._value;
if (currentValue < 0) {
slideIn();
} else if (currentValue > height - 300) {
handleClose();
}
},
})
).current;
...
return (
<View
testID={DRAWER_WRAPPER_TESTID}
{...panResponder.panHandlers}
>
...
</View>
);
And, finally, the unit test would be something like this
// pan-handling-component.test.tsx
...
const panHandler = instance.root.findByProps({
testID: DRAWER_WRAPPER_TESTID,
});
act(() => {
panHandler.props.onPanResponderMove(eventMock, gestureStateMOck);
panHandler.props.onPanResponderRelease();
});
...