I have the next code i made a test but now i have a dilema strictly speaking with tdd for add
"this.loadCounter('anotherReq', 'anotherError', differentCallback);"
i have to reply the tests only for test the behavior but i not sure if this is necessary.
class Statistic extends PureComponent<Props, State> {
state = {
};
componentDidMount() {
this.loadCounter('suggestedReqCount', 'hasSuggestedReqsErrors', getCountSuggestedReqs);
this.loadCounter('anotherReq', 'anotherError', differentCallback);
}
loadCounter = async (
stateCounterKey: string, stateCounterErrorKey: string, loaderFunction: Function) => {
try {
const count = await loaderFunction();
this.setState({
[stateCounterKey]: count,
});
} catch (ex) {
this.setState({
[stateCounterErrorKey]: true,
});
}
}
}
this are the test that i have
test('Should set state with suggested requirement count', async () => {
const wrapper = mount(
<Statistic
intl={{
formatMessage: jest.fn(),
}}
/>,
);
wrapper.update();
await getCountSuggestedReqs();
expect(wrapper.state().suggestedReqCount).toBe(5);
});
test('Should on fail load suggested reqs update state', async () => {
getCountSuggestedReqs.mockReturnValueOnce(Promise.reject('Error creado'));
const wrapper = mount(
<Statistic
intl={{
formatMessage: jest.fn(),
}}
/>,
);
wrapper.update();
await getCountSuggestedReqs();
expect(wrapper.state().hasSuggestedReqsErrors).toBe(true);
});
The first rule of TDD is that we cannot write any production code before having a failing test.
This alone implies that if you want to add production code, you have to write a test that will require it.
That said, there are cases where I won't write a unit test and let the larger tests grab that functionality, like in cases of simple delegation - the code isn't doing any logic, just passing the same arguments to another function, so there's nothing really to test, and the wiring will be tested from the outside.
In this case, however, it seems to me that you're invoking that function twice in order to get different information.
You could expand the existing test to include the new data, but I'm not a fan of that option. I would rather leave existing tests and add new tests for new functionality. Notice that "As the tests become more specific, the code becomes more generic", is true here too. Perhaps when the third call to loadCounter comes in, you'll need to refactor the code to be more generic, but at this point I would just add the second unit test.
All that said, you should definitely separate your logic code and your component code. There's no reason this unit test would be an Enzyme test. The code in componentDidMount could be easily refactored to a function that could be unit tested by just a plain ol' test.
Related
I am needing to spyOn window.location.assign for my unit test. But when I run the test I get this error.
Cannot spy the assign property because it is not a function; undefined given instead
Here is my code:
jest.spyOn(window.location, "assign");
Could anyone give me some hints or solutions on this case?
Since Jest v25 (Which uses a newer version of JSDOM) you will get the following error:
TypeError: Cannot assign to read only property 'assign' of object '[object Location]'
This is not a Jest/JSDOM bug by the way. This is normal browser behaviour and JSDOM tries to act like a real browser.
A workaround is to remove the location object, create your own one and after running your tests you should reset it to the original location object:
describe('My awesome unit test', () => {
// we need to save the original object for later to not affect tests from other files
const realLocation = global.location
beforeAll(() => {
delete global.location
global.location = { assign: jest.fn() }
// or even like this if you are also using other location properties (or if TypeScript complains):
// global.location = { ...realLocation, assign: jest.fn() }
})
afterAll(() => {
global.location = realLocation
})
it('should call location.assign', () => {
// ...your test code
expect(global.location.assign).toHaveBeenCalled()
// or even better:
// expect(global.location.assign).toHaveBeenCalledWith('/my_link')
})
})
As window can only be accessed through the global keyword in jest tests and window.location.assign is not implemented in jsdom, you can try
jest
.spyOn(global.location, "assign")
.mockImplementation(url => console.log(url))
When writing unit tests for a React Native project I want to be able to test different snapshots based on different platforms.
I first tried jest.mock to mock Platform but seems to be async. This approach does work when I have two separate files, but I'd prefer to keep everything in one file if possible.
I tried jest.doMock because of this snippet from the documentation:
When using babel-jest, calls to mock will automatically be hoisted to the top of the code block. Use this method if you want to explicitly avoid this behavior.
https://facebook.github.io/jest/docs/en/jest-object.html#jestdomockmodulename-factory-options
However I'm still seeing undesirable results. When I console.log in the android test I see that Platform.OS is whatever I set the first doMock to be.
I also tried wrapping the mock in a beforeEach in a describe becasue I thought that might help with scoping
http://facebook.github.io/jest/docs/en/setup-teardown.html#scoping
describe('ios test', () => {
it('renders ui correctly', () => {
jest.doMock('Platform', () => {
const Platform = require.requireActual('Platform');
Platform.OS = 'ios';
return Platform;
});
const wrapper = shallow(<SomeComponent />);
const tree = renderer.create(wrapper).toJSON();
expect(tree).toMatchSnapshot();
});
});
describe('android test', () => {
it('renders ui correctly', () => {
jest.doMock('Platform', () => {
const Platform = require.requireActual('Platform');
Platform.OS = 'android';
return Platform;
});
const wrapper = shallow(<SomeComponent />);
const tree = renderer.create(wrapper).toJSON();
expect(tree).toMatchSnapshot();
});
});
Any ideas on how I can change the mock Platform for tests in the same file?
There are a lot of suggestions on how to solve this problem in another question, but none of them worked for me either, given the same requirements you have (tests for different OSs in the same suite file and in one test run).
I eventually worked around it with a somewhat clunky trivial helper function that can be mocked as expected in tests – something like:
export function getOS() {
return Platform.OS;
}
Use it instead of Platform.OS in your code, and then simply mock it in your tests, e.g.
it('does something on Android', () => {
helpers.getOS = jest.fn().mockImplementationOnce(() => 'android');
// ...
}
That did the trick; credit for the idea is due to this guy.
In unit tests for a service, I have been putting asserts inside of service stubs, which has come in rather handy.
unit-test.js
let fooServiceStub = Ember.Object.extend({
fooMethod(bar) {
this.assert.ok(bar, 'fooMethod called with bar');
}
});
...
test('blah', function(assert) {
assert.expect(1);
let stubFooService = fooServiceStub.create({ assert });
let fooService = this.subject({
fooService: stubFooService
});
fooService.fooMethod('data');
});
Is an assert inside of a stub service possible for an acceptance/integration test?
The issue that I am running into is that for acceptance/integration tests, the way the service is injected is different from unit tests.
acceptance-test.js
let fooServiceStub = Ember.Service.extend({
fooMethod(bar) {
return 'baz';
}
});
....
beforeEach: function () {
this.application.register('service:mockFooService', fooServiceStub);
this.application.inject('controller', 'fooService', 'service:mockFooService');
}
I have not found a way to pass in the 'assert' object into such a stub.
To me, this is desirable to do during an acceptance test. The service goes off and does stuff that would be rather complicated to mock in the acceptance test, and I don't want to re-test my service. I just want to confirm the expected service calls were triggered.
You can just do something like this in your test:
this.set('fooService.FooMethod', bar => assert.ok(bar, 'bla'));
When I test a react component, what are the best practices and what things should I test for? In normal tests I usually just test if the correct state+input leads to the correct state+output
But React components are a bit different. They have state+props+userInput which result in state+markup.
This can lead to many, many different potential scenarios. Do I need to test for the resulting state of all those scenarios?
The markup can be huge. Should I test if the whole markup-tree is as expected? Or just part of it? How do I determine what part of the markup to test?
First obvious things to keep in mind:
If the logic of the your components can be encapsulated into modules and tested independently, then do it. Example: For a calculator component, the calculations themselves can be tested independently of the component. I know this is obvious, but just to make the point.
Break your components apart into smaller ones and test each of them granularly.
Regarding the component, always test:
If the correct props will render the correct output (HTML).
If the correct user interaction (click, key presses...) will fire the appropriate events and lead to the correct output (HTML). I usually don't deal with the component state at all during unit tests, as I don't find this a good practice. To test a TV you shouldn't have to open it.
If you are not sure about what library to use to test React components, I'd strongly recommend Enzyme.
Examples from their GitHub page:
describe('<MyComponent />', () => {
it('renders three <Foo /> components', () => {
const wrapper = shallow(<MyComponent />);
expect(wrapper.find(Foo)).to.have.length(3);
});
it('renders an `.icon-star`', () => {
const wrapper = shallow(<MyComponent />);
expect(wrapper.find('.icon-star')).to.have.length(1);
});
it('renders children when passed in', () => {
const wrapper = shallow(
<MyComponent>
<div className="unique" />
</MyComponent>
);
expect(wrapper.contains(<div className="unique" />)).to.equal(true);
});
it('simulates click events', () => {
const onButtonClick = sinon.spy();
const wrapper = shallow(
<Foo onButtonClick={onButtonClick} />
);
wrapper.find('button').simulate('click');
expect(onButtonClick).to.have.property('callCount', 1);
});
});
I am currently writing unit tests for one of my components. In particular, I have login(): void function. Here's the simplified logic:
login(): void {
this.showSpinner = true;
this.userService.login(loginData)
.subscribe(result => {
this.showSpinner = false;
}
)
}
I am struggling to write a test that checks that showSpinner property gets set to true before calling the userService.login.
Here's my test:
it('should display the spinner when the form is being saved',
inject([TestComponentBuilder], fakeAsync((tcb: any) => {
createComponent(tcb).then((fixture:ComponentFixture<any>) => {
fixture.componentInstance.login();
expect(fixture.componentInstance.showSpinner).toBe(true);
tick();
});
})));
});
And this test fails, because .subscribe gets resolved / run immediately (i tried commenting out this.showSpinner = false in my component, and the test passed).
In my userService mock, I have the following, for the login method mock:
this.loginSpy = this.spy('login').andReturn(Observable.of(this));
Where this is mockUserService.
I am confident that I am mocking userService and specifically the login method on the userService correctly, as I have other tests for this component that behave correctly.
I have also tried returning Observable.of(this).delay(1) from my spy and then calling tick(1) in my test. However that results in inconsistent behaviour in that sometimes my tests pass, but other times i get an error saying:
Error: 1 periodic timer(s) still in the queue.
How can I test the logic that precedes .subscribe()?
After more consideration I have realized that my current code does not abide by the single responsibility principle. This thought came from the fact that everyone is always repeating that you should "Refactor hard to test code".
With that in mind, I have moved all the logic that needed to be done before the call to userService.login is being made - into its own separate function. Which essentially results in:
login():void {
this.userService.login(this.loginData)
.subscribe(result => {
this.showSpinner = false;
});
}
formSubmit(): void {
this.showSpinner = true;
this.login();
}
This logic is now much easier to test.
HOWEVER we need to remember to add a spy on our login() method when we are testing formSubmit(), as if we don't, formSubmit() will simply make a call to login(), which will again complete synchronously and we will have the same problem. So my new and final test for this feature is:
it('should display the spinner when the form is being saved',
inject([TestComponentBuilder], fakeAsync((tcb: any) => {
createComponent(tcb).then((fixture:ComponentFixture<any>) => {
var loginSpy = spyOn(fixture.componentInstance, 'login');
fixture.componentInstance.formSubmit();
expect(fixture.componentInstance.showSpinner).toBe(true);
});
})));
});