How to write tests using the service scope - dart

My app is accessing object from the service scope (package:gcloud/service_scope.dart), like the storageService and additional services that I put inside the scope with ss.register().
Now I want to unit test a function that accesses this scope, and uses mock objects that I want to put in the service scope.
Is the only way to do so, to register them for every test, like this:
var withServiceScope = (callback()) => ss.fork(() {
// Register all services here
return callback();
});
test('the description', () => withServiceScope(() async {
// Call my function that can now access the service scope
}));
Or is there are way that allows me to do that in the setUp() function so I don't need to add this line for each test?

This might make it simpler to write your tests (code not tested)
import 'package:test/test.dart' as t;
import 'package:test/test.dart' show group;
var withServiceScope = (callback()) => ss.fork(() {
// Register all services here
return callback();
});
test(String description, Function testFunction) {
t.test(description, () => withServiceScope(() async {
testFunction();
}));
}
main() {
test('the description', () async {
// Call my function that can now access the service scope
}));
}

Related

How to pass Model as an argument in ipcMain.handle

I want to create a reusable function in Electron.js to handle Saving data irrespective of the model(e.g User, Employee, Product),so I passed Model as an argument, then call the specific Model during when the function is called.
but I get this error
Error: Expected handler to be a function, but found type 'object'
This is my code
const User = require( '../database/models/Users.js');
ipcMain.handle('user:create', saveData(User));
async function saveData(_, data,Model) {
try {
const user = await Model.insert(data);
return user;
} catch (e) {
console.log(e.message);
}
}
ipcMain.handle('user:create', saveData(User)); call function saveData(User) after app is started and it returns object. if you want to assign function to 'user:create' then without parameters it's ipcMain.handle('user:create', saveData); but with parameters it's.
ipcMain.handle('user:create', () => saveData(User));
is the same as
ipcMain.handle('user:create', function () {
return saveData(User)
});

Why is there no spy functionality in Mockito Dart?

The following code is a simplified example from my code. I have class A which is dependent on class B. I want to test class A, so I mock class B. Then I'm writing a test for a method of class A and inside of that test I write a stub for whenever a method from my mocked class B is called:
fetchData() async {
try {
await b.getData();
} on DioError catch (e) {
switch (e.response!.statusCode) {
case 401:
logout();
throw UnauthorizedException();
default:
throw UnspecifiedDioException(error: e);
}
}
Test written for fetchData() method:
test('check if fetchData calls logout when 401 is returned', () {
when(mockB.getData())
.thenAnswer((_) async =>
throw DioError(
requestOptions: RequestOptions(path: ""),
response: Response(requestOptions: RequestOptions(path: ""), statusCode: 401)));
verify(a.logout()); // doesn't work because A isn't mocked
});
I've read that you can do this very easily with spies but to my surprise spies are available for every language which uses mockito except for dart. It's apparently deprecated but then again how can something be deprecated if there isn't even a newer version to replace it with.
I'd really appreciate it if someone could tell me if there is a convenient workaround for what I'm trying to achieve. Thanks in advance.
Edit: I've changed the question because the former one wasn't making much sense. I just wanna know if there is something like spies in dart or not.
Using mocktail..
You should stub your logout invocation's dependency as well.
class A {
A({required this.api, required this.auth});
// to be mocked
final Api api;
final Auth auth;
Future<void> fetchData() async {
try {
await api.getData();
} catch (e) {
auth.logout();
}
}
}
class Auth {
Future<void> logout() => Future(() {});
}
class Api {
Future<void> getData() => Future(() {});
}
And your test
class MockApi extends Mock implements Api {}
class MockAuth extends Mock implements Auth {}
void main() {
// create mock objects
final mockApi = MockApi();
final mockAuth = MockAuth();
test('when [Api.getData] throws, [Auth.logout] is called', () async {
// create an instance of "A" and use your mock objects
final a = A(api: mockApi, auth: mockAuth);
// use "thenThrow" to throw
when(() => mockApi.getData()).thenThrow('anything');
// use "thenAnswer" for future-returning methods
when(() => mockAuth.logout()).thenAnswer((_) => Future.value(null));
// call the method to "start" the test
await a.fetchData();
// verify logout was called
verify(mockAuth.logout).called(1); // passes
});
}

How to add something like isClickable() in my appium native app tests

How to add something like isClickable() in my appium native app tests. I have written my tests, however they are very flaky and fail sometimes because it cannot find the element. I am thinking about making custom click and set value functions with the implicit wait times.
I thought about using isClickable() but the appium documentation says - Please note that isClickable works only in web and webviews, it doesn't work in mobile app native context.
Is there any other alternative i can use? can i use smartwait? if yes how can i implement that
Here is how i am defining home.screen.js
import AppScreen from './app.screen';
const SELECTORS = {
HOME_SCREEN: '~homeBarButton',
PRODUCTSEARCH_SCREEN: '~productSearchBarButton',
CUSTOMERSEARCH_SCREEN: '~customersBarButton',
STOREHUB_SCREEN: '~storeHubBarButton',
SETTING_ICON: '~SettingsIcon',
LOGOUT_BUTTON: '~settingsMainLogoutButton'
};
class HomeScreen extends AppScreen {
constructor () {
super(SELECTORS.HOME_SCREEN);
}
get homescreenButton () {
return $(SELECTORS.HOME_SCREEN);
}
get productsearchField () {
return $(SELECTORS.PRODUCTSEARCH_SCREEN);
}
get customersearchButon () {
return $(SELECTORS.CUSTOMERSEARCH_SCREEN);
}
get storehubButon () {
return $(SELECTORS.STOREHUB_SCREEN);
}
get settingIcon () {
return $(SELECTORS.SETTING_ICON);
}
get logoutButton () {
return $(SELECTORS.LOGOUT_BUTTON);
}
}
export default new HomeScreen();
And i am writing my test like this test.js:
import HomeScreen from '../screenobjects/home.screen';
import FormScreen from '../screenobjects/forms.screen';
import CommonPage from '../pageobjects/common.page';
describe('Sending item successfullt,', () => {
beforeEach(() => {
CommonPage.login()
});
afterEach(() => {
CommonPage.logout()
});
it('should be able to send the item to the mirror', () => {
driver.pause(3000)
HomeScreen.productsearchField.click();
driver.pause(3000)
HomeScreen.customersearchButon.click();
});
});
As you can see above, I have to add driver.pause otherwise my tests would fail because of button not clickable or typeable.
My suggestion is that you can get your elements attribute clickable and if its true keep doing your things
public boolean isClickable(String element) {
return androidDriver.findElementByAccessibilityId(element).getAttribute("clickable").equals("true");
}
You can use any method to find your element.
Best approach is to stop using implicit waits and do an explicit wait before each driver UI interaction.
You should do some reading on waitUntil / WebDriverWait (not sure if you have that in node.js implementation).
Then create functions for interacting with all types of elements in your app that perform an explicit wait before execution.
Pseudo code:
get clickButton (Selector element) {
waitUntil(clickable(element),...);
return $(driver.click(element));
}
Write generic methods for all type of elements in your app (button, textfield, dropdown...) and remove implicit waits from driver. You will see a big difference in your test stability.

How can I extend functions in Dart?

I know about the extension feature in Dart, but how can I use it with functions?
Essentially, I am facing two problems:
What do I extend on (extension FancyFunction on ?)?
I would want to add a function like toAsync that makes the function return a Future of its usual result.
How would I implement calling?
I could create a callAsync member that executes this() or this.call(), but I would like to use regular calling syntax, i.e. just parentheses.
What do I extend on when extending functions?
Dart has a Function type. This can be extended on and you can pass type parameters if you want.
Here is an example from the changelog:
extension CurryFunction<R, S, T> on R Function(S, T) { ... }
Furthermore, you can extend any typedef of a function.
For adding the toAsync and callAsync functionality a generic return type R will do. Note that this will only extend functions without parameters as Function() takes no parameters:
extension FancyFunction<R> on R Function() {
Future<R> Function() toAsync() => () async => this();
}
Now, this could be used like this:
void syncHello() => print('Hello');
void main() {
final asyncHello = syncHello.toAsync();
asyncHello().then((_) => print(', World!'));
}
How do I implement calling?
Every class in Dart can implement the call method. You can either execute this method simply using parentheses or with .call().
Here is an example implementation:
extension FancyFunction<R> on R Function() {
Future<R> call() async => this();
}
Since every Function already implements call, the extension member cannot be called implicitly.
Instead, you will have to explicitly declare your function as a FancyFunction to be able to call it:
void syncHello() => print('Hello');
void main() {
FancyFunction(syncHello)()
.then((_) => print(', World!'));
}
Note that FancyFunction(syncHello)() is the same method call as FancyFunction(syncHello).call().
However, now we have two problems:
We have to explicitly declare our function to be a FancyFunction, which somewhat defeats the purpose of having an extension.
The call method will always return a Future as the regular call method cannot be accessed anymore when we declare a FancyFunction.
Instead, adding a method like callAsync seems like a better solution:
extension FancyFunction<R> on R Function() {
Future<R> callAsync() async => this();
}
Now, callAsync can be used implicitly again:
void syncHello() => print('Hello');
void main() {
syncHello.callAsync()
.then((_) => print(', World!'));
syncHello(); // regular call
}

Dart - how to mock a method that returns a future

I have a class that defines a method that returns a Future. The Future contains a list of class that also return a future.
class User{
Future<List<Album>> albums(){
};
}
class Album{
Future<List<Photos>> photos(){
}
};
What is the best way to mock the method in these classes when testing another class?
The class I am trying to test looks a bit like
class Presenter {
Presenter( User user){
user.albums().then( _processAlbums);
}
_processAlbums(List<Album> albums) {
albums.forEach( (album)=>album.photos.then( _processPhotos));
}
_processPhotos(List<Photo> photos) {
....stuff
}
}
I tried writing a unit test like this
class MockUser extends Mock implements User{}
class MockAlbum extends Mock implements Album{}
class MockPhoto extends Mock implements Photo{}
class MockFutureList<T> extends Mock implements Future<T>{
MockFutureList( List<T> items){
when( callsTo( "then")).thenReturn( items);
}
}
void main(){
test("constuctor should request the albums from the user ",(){
MockUser user = new MockUser();
MockAlbum album = new MockAlbum();
List<Album> listOfAlbums = [ album];
MockPhoto photo = new MockPhoto();
List<Album> listOfPhotos = [ album];
user.when( callsTo( "albums")).thenReturn( new MockFutureList(listOfAlbums));
album.when( callsTo( "photos")).thenReturn( new MockFutureList( listOfPhotos));
PicasaPhotoPresentor underTest = new PicasaPhotoPresentor( view, user);
user.getLogs( callsTo( "albums")).verify( happenedOnce);
album.getLogs( callsTo( "photos")).verify( happenedOnce);
});
}
This allowed me to test that the constructor called the user.photos() method, but not that the album.photos() method was called.
I am not sure that mocking a Future is a good idea - Would it not be better to create a 'real' Future that contains a list of Mocks?
Any ideas would be very helpful!
Since you're only interested in verifying that methods in User and Album are called, you won't need to mock the Future.
Verifying the mocks gets a bit tricky here, because you're chaining futures inside the constructor. With a little understanding of how the event loop works in Dart, I recommend using a future and calling expectAsync after you create your presenter.
The expectAsync function tells the unit test library to wait until it's called to verify your tests. Otherwise the test will complete successfully without running your expectations.
With this, here's what your test should would look like:
import 'package:unittest/unittest.dart';
class MockUser extends Mock implements User {}
class MockAlbum extends Mock implements Album {}
void main() {
test("constuctor should request the albums from the user ", () {
var user = new MockUser();
var album = new MockAlbum();
user.when(callsTo("albums")).thenReturn(new Future(() => [album]));
var presenter = new PicasaPhotoPresentor(view, user);
// Verify the mocks on the next event loop.
new Future(expectAsync(() {
album.getLogs(callsTo("photos")).verify(happendOnce);
}));
});
}
Here is how I managed to do it
1) Define FutureCallbackMock
class FutureCallbackMock extends Mock implements Function {
Future<void> call();
}
2) get function from a mock and set it up
FutureCallback onPressed = FutureCallbackMock().call;
completer = Completer<void>();
future = completer.future;
when(onPressed()).thenAnswer((_) => future);
3) Verify like so
verify(onPressed()).called(1);
4) Complete the future if needed:
completer.complete();
NOTE: in flutter tests I had to wrap my test in tester.runAsync like so
testWidgets(
'when tapped disables underlying button until future completes',
(WidgetTester tester) async {
await tester.runAsync(() async {
// test here
});
});
I was able to do this with Mocktail. This is the article that this is from, and explains how to integrate it into your app. This is a full widget test and depends on this gist code.
The crux is that you need to declare a Mock class that has a call method. Then, you can then mock the top-level function that returns a Future. You can use the when and verify methods with this.
//Gist code
import 'package:gist/main.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter/material.dart';
class LaunchMock extends Mock {
Future<bool> call(
Uri url, {
LaunchMode? mode,
WebViewConfiguration? webViewConfiguration,
String? webOnlyWindowName,
});
}
void main() {
testWidgets('Test Url Launch', (tester) async {
//These allow default values
registerFallbackValue(LaunchMode.platformDefault);
registerFallbackValue(const WebViewConfiguration());
//Create the mock
final mock = LaunchMock();
when(() => mock(
flutterDevUri,
mode: any(named: 'mode'),
webViewConfiguration: any(named: 'webViewConfiguration'),
webOnlyWindowName: any(named: 'webOnlyWindowName'),
)).thenAnswer((_) async => true);
final builder = compose()
//Replace the launch function with a mock
..addSingletonService<LaunchUrl>(mock);
await tester.pumpWidget(
builder.toContainer()<MyApp>(),
);
//Tap the icon
await tester.tap(
find.byIcon(Icons.favorite),
);
await tester.pumpAndSettle();
verify(() => mock(flutterDevUri)).called(1);
});
}

Resources