NestJS Jest test expecting then catch finally - unit-testing

I am new to NestJS and have written a basic unit test for my service/controller using an example from a couple of sites including the official NestJS website and even before I try to run this code,
it('should be defined', async () => {
const result:MyEntity[] = [{"test1":"value1"},{"test1":"value2"}];
jest.spyOn(service, 'findAll').mockImplementation(() => result);
expect(await controller.findAll()).toBe(result);
});
I see the following error.
Type 'MyEntity[]' is missing the following properties from type 'Promise<MyEntity[]>': then, catch, [Symbol.toStringTag], finallyts(2739)
index.d.ts(1163, 33): The expected type comes from the return type of this signature.
I am returning a Promise from my controller but somehow the compiler is expecting a try catch finally somewhere but I don't know where this should go.

If service.findAll normally returns a promise, you should make the mock return a promise as well. You can do this with jest.spyOn(service, 'findAll').mockResolvedValue(result). Now in your test you can do expect(controller.findAll()).resovles.toEqual(result) to make the method resolve properly and test the result.

Related

Is it considered good practice to use expect.assertions in tests with Jest? [duplicate]

I've found a lot of this sort of thing when refactoring our Jest test suites:
it('calls the API and throws an error', async () => {
expect.assertions(2);
try {
await login('email', 'password');
} catch (error) {
expect(error.name).toEqual('Unauthorized');
expect(error.status).toEqual(401);
}
});
I believe the expect.assertions(2) line is redundant here, and can safely be removed, because we already await the async call to login().
Am I correct, or have I misunderstood how expect.assertions works?
expect.assertions is important when testing the error scenarios of asynchronous code, and is not redundant.
If you remove expect.assertions from your example you can't be confident that login did in fact throw the error.
it('calls the API and throws an error', async () => {
try {
await login('email', 'password');
} catch (error) {
expect(error.name).toEqual('Unauthorized');
expect(error.status).toEqual(401);
}
});
Let's say someone changes the behavior of login to throw an error based on some other logic, or someone has affected the mock for this test which no longer causes login to throw. The assertions in the catch block won't run but the test will still pass.
Using expect.assertions at the start of the test ensures that if the assertions inside the catch don't run, we get a failure.
This is from Jest documentation:
Expect.assertions(number) verifies that a certain number of assertions
are called during a test. This is often useful when testing
asynchronous code, in order to make sure that assertions in a callback
actually got called.
So to put in other words, expect.assertions makes sure that the n number of assertions are made by the end of the test.
It's good to use it especially when writing a new tests, so one can easily check that correct assertions are made during the test. Async tests often pass because the intended assertions were not made before the test-runner (Jest,Mocha etc.) thought the test was finished.
I think we are missing the obvious here.
expect.assertions(3) is simply saying ...
I expected 3 expect statements to be called before the test times out. e.g.
expect(actual1).toEqual(expected1);
expect(actual2).toEqual(expected2);
expect(actual3).toEqual(expected3);
This timing out business is the reason to use expect.assertions. It would be silly to use it in a purely synchronous test. At least one of the expect statements would be found in a subscribe block (or other async block) within the spec file.
To ensure that the assertions in the catch block of an async/await test are adequately tested, expect.assertions(n) must be declared as shown in your code snippet. Such declaration is unnecessary for async/await tests without the catch block.
It seems quite unintuitive but it is simply the way it is. Perhaps, for certain reasons well deep within the javascript runtime, the test environment can detect when an await'ed' promise successfully resolved but cannot detect same for await'ed' promises that failed to resolve. The creators of the test environment would likely know verbatim why such is the case.
I have to admit that apart from error testing, I find it challenging to see a real use for expect.assertions. The above snippet can be changed to the following with the same guarantee but I think it reads more naturally and doesn't require me to count how many time I call expect. This is especially error-prone if a test if complex:
it('calls the API and throws an error', async () => {
try {
await login('email', 'password');
fail('must throw')
} catch (error) {
expect(error.name).toEqual('Unauthorized');
expect(error.status).toEqual(401);
}
});

Checking middleware is called from http call

How would one test that a piece of custom middleware is actually called from a standard HTTP event?
ie. The middleware is called from:
MyController.js
router.get('/some/endpoint', [myMiddleware()], (req, res, next) => {
// Code to do whatever here
});
The middleware itself can be defined as:
MyMiddleware.js
module.exports = () => {
// Middleware code in here
}
My quest is to check that the middleware is called once from my unit test, but I cannot find documentation around this.
MyTest.test.js
it('Should return whatever from GET call', () => {
return request(app).get('/some/endpoint')
.expect(200)
.expect(res => {res.body.should.deep.equal(bodyValue)});
// How would I place code in here to check that MyMiddleware is called?
// ie. sinon.assert.calledOnce(MyMiddleware)
});
I have thought about using Sinon's spy, but I can't think of how to hook into the middleware... My attempt was this:
const mwSpy = sinon.spy(require('path to middleware file'));
sinon.assert(calledOnce(mwSpy));
The usual way of going about this is splitting this into two tests, an integration test and a unit test.
Will the middleware I specified in the router.get call end up being called when someone hits this endpoint?
Does my middleware do the right thing?
The first part is basically testing that the Express API is doing what the documentation says. That's not what unit tests are for (this was tagged unit-testing), but since you are already using HTTP requests to test the endpoint, I guess that's not what you are after anyway: you are basically creating verification tests for your system.
You could still test the Express routing without HTTP, though, as I detail in the answer to this question, concerning how to test the router programmatically (faster tests, no http), but less just stick to what you have.
So the basic question is: "My quest is to check that the middleware is called once from my unit test". You don't seem to concern yourself with whether the middleware is doing the right thing or not, just that it's called, which calls for the question on whether we should test the middleware or the layer using the middleware.
In both cases, you need to find a way of injecting a test spy. Either you write a small utility method that will inject that spy: function setMiddleware(module){ middleware = module; } or you use some tooling like proxyquire. See this tutorial on Sinon's homepage for background.
I would just do this (in the test code):
it('Should return whatever from GET call', () => {
var middlewareFake = sinon.fake();
// I am assuming it's the actual app object you are referencing below in the request(app) line
var app = proxyquire('../app/index.js', { './my-middleware': middlewareFake });
//
return request(app).get('/some/endpoint')
.expect(200)
.expect(res => {
res.body.should.deep.equal(bodyValue)
expect(middlewareFake).was.called;
});
});

Mocking Observable with Jest - rxjs

I have a simple Observable piping from another Observable that I want to test.
const loginState$ = messageBusObservables.loginState$.pipe(
startWith({ isLoggedIn: false })
pluck('isLoggedIn'),
distinctUntilChanged()
)
messageBusObservables is an object of observables. Where loginState$ is an Observable.
In my tests, I thought I would easily be able to mock the './messageBus' module like this: (how the module is imported is irrelevant, but import preferred)
import { of } from 'rxjs'
import './messageBus'
jest.mock('./messageBus', () => ({
loginState$: of({ isLoggedIn: true }),
}))
However, Jest throws the error:
babel-plugin-jest-hoist: The module factory of jest.mock() is not allowed to reference any out-of-scope variables.
Invalid variable access: of
I have tried, putting it in a jest.fn() I have tried extracting of({ isLoggedIn: true }) to a variable. But I keep getting the same error from jest.
So how can I mock the input into my Observables using Jest? I'll run into the same problem with other observables using .merge, .zip etc.
It needs to be a real observable that is the input of my other observables. I just want to mock the value with something like of() rather than mocking an object, with a method on it, that returns an object with a .pipe method etc. (I don't want to mock the functionality of an Observable). I want to pass it a real observable with a value set in my unit test.
I also need these mocks to be dynamic. So the mock from 1 assertion can be different from the mock in the next assertion. (clearing them with something like a beforeEach)
EDIT:
I also tried to use babel-plugin-rewire to mock this module, this worked fine in the *.test.js file where I was mocking it. But in the actual file no matter what I set the export to using rewire, it always got imported as the original Observable.
the reason you are getting this message:
babel-plugin-jest-hoist: The module factory of jest.mock() is not allowed to reference any out-of-scope variables. Invalid variable access: of
is because jest automatically hoists calls to jest.mock so that they happen before the imports.
You have two options to get around this default behaviour, the simple way is to use jest.doMock which is NOT hoisted:
jest.doMock('./messageBus', () => ({
loginState$: of({ isLoggedIn: true }),
}))
Alternatively, you can prefix all the variables referenced inside the mock factory passed to jest.mock with "mock":
const mockMessageBus = {
loginState$: of({ isLoggedIn: true }),
}
jest.doMock('./messageBus', () => mockMessageBus)
(note that you are responsible for ensuring all mock variables referenced in the factory function are in scope when jest.mock is called)
You're close.
You are trying to mock the module by passing a module factory as the second parameter to jest.mock. The main constraint of that approach is that the module factory must be completely self-contained and "is not allowed to reference any out-of-scope variables".
Referencing of from rxjs in the module factory (as you have found) breaks that constraint and causes the error you are seeing.
Fortunately there are other ways to mock modules.
From what I can see of your code it looks like the easiest approach would be to create a Manual Mock of the messageBus module.
Create a __mocks__ folder in the same directory as messageBus.js and create the mock (also called messageBus.js) within the __mocks__ folder.
__mocks__/messageBus.js will look something like this:
import { of } from 'rxjs'
export default {
loginState$: of({ isLoggedIn: true })
}
Then tell Jest you want to use the manual mock within your test by calling
jest.mock('messageBus');
at the top of your test file.
That call is hoisted by Jest and ensures that any code that imports messageBus during the test will get the mocked module.

Mocha/Chai - test specific setup and tear down

I know mocha has global before and after, and each-test before and after, but what I would like is test-specific before and after. Something like SoapUI has.
For example, say that I have a test checking that the creation of a user works.
I want to remove the user, should it exist, from the database BEFORE the test. And I want the test to ensure that the user is removed AFTER the test. But I do not want to do this for EACH test, as only one test will actually create the user. Other tests will delete user/s, update user/s, fail to create an already existing user etc.
Is this possible, or do I have to include the setup and tear down code in the test? If so, how do I ensure that both the setup and tear down executes properly, independent of the test result?
For tests where I need to have special setup and teardown code but that are not otherwise distinguishable from their siblings, I just put a describe block with an empty title:
describe("SomeClass", () => {
describe("#someMethod", () => {
it("does something", () => {});
it("does something else", () => {});
describe("", () => {
// The before and after hooks apply only to the tests in
// this block.
before(() => {});
after(() => {});
it("does something more", () => {});
});
});
});
Is this possible, or do I have to include the setup and tear down code in the test? If so, how do I ensure that both the setup and tear down executes properly, independent of the test result?
You can put setup and tear down code in the test itself (i.e. inside an the callback you pass to it). However, Mocha will treat any failure there as a failed test, period. It does not matter where in the callback passed to it the failure occurs. Assertion libraries allow you to provide custom error messages which can help you figure out what exactly failed, but Mocha will see all failures in it the same way: the test failed. If you want Mocha to treat failures in setup/teardown code differently from test failures, then you have to use the hooks as I've shown above.

Sinon stub instance method declared in mapDispatchToProps

New to testing and React Redux, so I may be conflating a few issues here. I will only present one example, but I have tried many different combinations of mount(), shallow(), instance(), stub, spy and more.
Given a component, where setFooData() updates redux state and Foo.props.data:
const mapDispatchToProps = (dispatch, props) => ({
setFooData(fooId, data) {
dispatch(Actions.setFooData(fooId, data));
},
});
...
return (
<div fooId={this.props.fooId}>
<Foo {...fooProps}/>
</div>
);
I would like to write some tests around the conditions under which setFooData() is called, namely conditions in lifecycle methods like componentDidMount() and componentWillReceiveProps().
Because setFooData() involves server calls and more, and because these tests merely concern the view layer and how the component renders as a result of Foo.props.data being set eventually by setFooData(), setFooData() seems like a good candidate for stub.
Therefore, Enzyme's shallow(), rather than mount(), seems appropriate, correct? In any case, when I try to stub setFooData():
let wrapper = return shallow(<Foo {...props}/>);
let stub = sinon.stub(wrapper.instance(), 'setFooData');
I receive the error:
Attempted to wrap undefined property setFooData as function
Upon inspection, wrapper.instance() yields an object where setFooData() is indeed not defined, but according to other examples, I would think it should be.
Furthermore, setFooData() does exist on wrapper.instance().selector.props, and while let stub = sinon.stub(wrapper.instance().selector.props, 'setFooData'); avoids the error, when I inspect the object setFooData() =/= stub, and the function is not called as per the test.
When I use mount() instead,
let wrapper = mount(<Provider store={store}><Foo {...props}/></Provider>);
let componentDidMountSpy = sinon.spy(Foo.prototype, 'componentDidMount');
let componentWillReceivePropsSpy = sinon.spy(Foo.prototype, 'componentWillReceiveProps');
expect(componentDidMountSpy.called).to.be.true; //passes
expect(componentWillReceivePropsSpy.called).to.be.true; //passes
expect(stub.called).to.be.true; //fails
I receive a different error that appears related to the body of setFooData(), so setFooData() is called but the function is not actually stubbed to prevent its real body from being executed.
Thanks for any help to clarify my understanding.
I think you're taking the hardest path. You should test your component in isolation, not the connected one. If you test the connected component you're making an integration test and double testing that connect indeed works. That's already tested in react-redux for you.
Instead, test your action creators by themselves in unit tests.
Then, export your component as named export without connecting and use the default export for the connect version.
That way you can simply import the pure-React version and pass anything you want as props, in order to make easy assertions afterwards.
If you need to specifically test that something happens in those lifecycle methods, you can call those methods from the instance:
const fakeActionCreator = sinon.spy()
const subject = mount(<MyComponent doSomething={ fakeActionCreator } />)
subject.instance().componentDidMount()
assert.equal(fakeActionCreator.callCount, 1)