I'm looking for an way to mock/stub stores that are used in Svelte components, specifically with the auto-subscribe syntax (ie $myStore or $myStore.property).
I've tried a couple of different mocks, mostly along the lines of the following:
jest.mock('./stores/users', () => ({
currentUser: jest.fn().mockImplementation(() =>
writable({
isAdmin: true,
}),
),
}));
The test will error out on any auto-subscribed references to the store, which are undefined. For example, if I am referencing a store from "../../stores/users" in my component like so...
{#if $user.isAdmin}
<div>Hello I'm an admin</div>
{/if}
... then I receive TypeError: Cannot read property 'isAdmin' of undefined
It seems that even when mocking a Svelte store, the $ auto-subscribe syntax is not breaking the test. I should point out that when logging the mocked store, the expected Svelte store methods (subscribe(), set() and update()) are there, and executing the subscribe() method exposes the isAdmin attribute also as expected.
I can't seem to find any established unit testing patterns that deal with Svelte and the use of stores, so any and all input on the topic would be greatly appreciated!
Related
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.
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)
I'm trying to unit test my controllers in Laravel 5, but have serious issues wrapping my head around it. It seems as I have to trade the great short-hand functions and static classes for dependency injected equivalents if I actually want to do isolated unit testing.
First off, what I see in the documentation as "Unit testing", is not unit testing to me. It seems more like functional testing. I can not test a controller function isolated, as I will have to go through the entire framework, and will need to actually seed my database if I have any code interacting with my database.
So, in turn, I want to test my controllers isolated of the framework. This is however proving to be quite difficult.
Let's look at this example function (I've kept out some parts of this function for the sake of the question):
public function postLogin(\Illuminate\Http\Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials, $request->has('remember')))
{
return redirect()->intended($this->redirectPath());
}
}
Now, the problem arises in the final lines. Sure, I can mock the Request instance that's sent to the function, that's no issue. But how am I going to mock the Auth class, or the redirect function? I need to rewrite my class/function with dependency injection like this:
private $auth;
private $redirector;
public function __construct(Guard $auth, \Illuminate\Routing\Redirector $redirector)
{
$this->auth = $auth;
$this->redirector = $redirector;
}
public function postLogin(\Illuminate\Http\Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $request->only('email', 'password');
if ($this->auth->attempt($credentials, $request->has('remember')))
{
return $this->redirector->intended($this->redirectPath());
}
}
And I end up with a convoluted unit test, full of mocks:
public function testPostLoginWithCorrectCredentials()
{
$guardMock = \Mockery::mock('\Illuminate\Contracts\Auth\Guard', function($mock){
$mock->shouldReceive('attempt')->with(['email' => 'test', 'password' => 'test'], false)->andReturn(true);
});
$redirectorMock = \Mockery::mock('\Illuminate\Routing\Redirector', function($mock){
$mock->shouldReceive('intended')->andReturn('/somePath');
});
$requestMock = \Mockery::mock('\Illuminate\Http\Request', function($mock){
$mock->shouldReceive('only')->with('email', 'password')->andReturn(['email' => 'test', 'password' => 'test']);
$mock->shouldReceive('has')->with('remember')->andReturn(false);
});
$object = new AuthController($guardMock, $redirectorMock);
$this->assertEquals('/somePath', $object->postLogin($requestMock));
}
Now, if I had any more complex logic that would, for example, use a model, I'd have to dependency inject that as well, and mock it in my class.
To me, it seems like either, Laravel isn't providing what I want it to do, or my testing logic is flawed. Is there any way I can test my controller functions without getting out-of-control testing functions and/or having to depedency inject standard Laravel classes, available to me anyways in my controller?
You shouldnt be trying to unit test controllers. They are designed to be functional tested via calling them through the HTTP protocol. This is how controllers are designed, and Laravel provides a number of framework assertions you can include in your tests to ensure they work as expected.
But if you want to unit test the application code contained within the controller, then you should actually consider using Commands.
Using Commands allows you to extracte the application logic from your controller into a class. You can then unit test the class/command to ensure you get the results expected.
You can then simply call the command from the controller.
In fact the Laravel documentation tells you this:
We could put all of this logic inside a controller method; however, this has several disadvantages... it is more difficult to unit-test the command as we must also generate a stub HTTP request and make a full request to the application to test the purchase podcast logic.
I'm writing a simple qunit test that has a controller setup w/ a few items in an array
App.UploadController = Ember.Controller.extend({
to_upload: Ember.A([])
});
I add stuff to this "to_upload" array and verify it between tests. One issue I've found that that if I don't "manually clear it" like this in my test "tear down" it holds onto this state (even with an App.reset() being called in each teardown (calling destroy).
App.__container__.lookup("controller:upload").clear_files()
(the clear files is a simple monkey patch I added in my test helper -not production code)
App.UploadController.reopen
clear_files: function() {
this.get('to_upload').clear();
}
Here is my tear down for the qunit tests
module "/upload",
teardown: function() {
App.reset()
App.__container__.lookup("controller:upload").clear_files()
}
It's probably related to the fact that assigning an array to a property in the class definition makes it a class property and not an instance property. I'm not sure if this is documented somewhere, I've read it before a few times though. I'll update with documentation if I can find it.
Until then you can probably prove my theory by modifying the code to look like this.
App.UploadController = Ember.Controller.extend({
init: function(){
this.set('to_upload', Em.A([]));
this._super();
},
to_upload: null
});
You can read about it in step 6 here http://codebrief.com/2012/03/eight-ember-dot-js-gotchas-with-workarounds/
I'm pretty sure this should be put somewhere in the docs, I'm not sure where the most obvious would be though.
Just begun creating some unit tests for my controllers within my CakePHP application however I am completely flummoxed on how I should create a mock for the Auth Component, I have read the CookBook and thought I had it right, but keep getting thrown this error.
Error: Call to a member function allow() on a non-object
Within the controller I am testing there is a beforeFilter function with the following code:
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow('create');
}
Within my test I have included the following:
$Leagues = $this->generate('Leagues', array(
'components' => array(
'Auth'
)
));
I have played around with staticExpects() also but it doesn't seem to have much affect (I am also unsure what I need to put in to staticExpects()).
What do you mean by mock? Just stuffing an object with data that you set? The error you're getting is because you need to use
$this->Auth->allow(array('create'))
Found the issue - I have included the Auth component within AppController.php, and not specifically within the controller I was trying to test. By including it with my controller specifically the errors have gone away.