Hi hope everyone stays safe. Thanks for this nice community. I have done my Flutter&Dart app and it works. But i need to run some test on flutter app so I found one example that is similar to mine on one of the files that I created in my flutter app and it is unit testing. Now I watched and read the flutter docs for widget testing or some other examples for unit testing that would help me. But no luck so if there is someone that could show me an example on my dart file and the code down below.
import 'dart:convert';
import 'dart:async';
import 'dart:io';
import 'package:http_parser/http_parser.dart';
import 'package:aplikacija/models/location_data.dart';
import 'package:scoped_model/scoped_model.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:rxdart/subjects.dart';
import 'package:mime/mime.dart';
import '../models/product.dart';
import '../models/user.dart';
import '../models/auth.dart';
import '../models/location_data.dart';
mixin ConnectedProductsModel on Model {
List<Product> _products = [];
String _selProductId;
User _authenticatedUser;
bool _isLoading = false;
}
mixin ProductsModel on ConnectedProductsModel {
bool _showFavorites = false;
List<Product> get allProducts {
return List.from(_products);
}
List<Product> get displayedProducts {
if (_showFavorites) {
return _products.where((Product product) => product.isFavorite).toList();
}
return List.from(_products);
}
int get selectedProductIndex {
return _products.indexWhere((Product product) {
return product.id == _selProductId;
});
}
String get selectedProductId {
return _selProductId;
}
Product get selectedProduct {
if (selectedProductId == null) {
return null;
}
return _products.firstWhere((Product product) {
return product.id == _selProductId;
});
}
bool get displayFavoritesOnly {
return _showFavorites;
}
Future<Map<String, dynamic>> uploadImage(File image,
{String imagePath}) async {
final mimeTypeData = lookupMimeType(image.path).split('/');
final imageUploadRequest = http.MultipartRequest(
'POST',
Uri.parse(
'https://us-central1-flutter-aplikacija.cloudfunctions.net/storeImage'));
final file = await http.MultipartFile.fromPath(
'image',
image.path,
contentType: MediaType(
mimeTypeData[0],
mimeTypeData[1],
),
);
imageUploadRequest.files.add(file);
if (imagePath != null) {
imageUploadRequest.fields['imagePath'] = Uri.encodeComponent(imagePath);
}
imageUploadRequest.headers['Authorization'] =
'Bearer ${_authenticatedUser.token}';
try {
final streamedResponse = await imageUploadRequest.send();
final response = await http.Response.fromStream(streamedResponse);
if (response.statusCode != 200 && response.statusCode != 201) {
print('Something went wrong');
print(json.decode(response.body));
return null;
}
final responseData = json.decode(response.body);
return responseData;
} catch (error) {
print(error);
return null;
}
}
Future<bool> addProduct(String title, String description, File image,
double price, LocationData locData) async {
_isLoading = true;
notifyListeners();
final uploadData = await uploadImage(image);
if (uploadData == null) {
print('Upload failed!');
return false;
}
final Map<String, dynamic> productData = {
'title': title,
'description': description,
'price': price,
'userEmail': _authenticatedUser.email,
'userId': _authenticatedUser.id,
'imagePath': uploadData['imagePath'],
'imageUrl': uploadData['imageUrl'],
'loc_lat': locData.latitude,
'loc_lng': locData.longitude,
'loc_address': locData.address
};
try {
final http.Response response = await http.post(
'https://flutter-aplikacija.firebaseio.com/products.json?auth=${_authenticatedUser.token}',
body: json.encode(productData));
if (response.statusCode != 200 && response.statusCode != 201) {
_isLoading = false;
notifyListeners();
return false;
}
final Map<String, dynamic> responseData = json.decode(response.body);
final Product newProduct = Product(
id: responseData['name'],
title: title,
description: description,
image: uploadData['imageUrl'],
imagePath: uploadData['imagePath'],
price: price,
location: locData,
userEmail: _authenticatedUser.email,
userId: _authenticatedUser.id);
_products.add(newProduct);
_isLoading = false;
notifyListeners();
return true;
} catch (error) {
_isLoading = false;
notifyListeners();
return false;
}
}
/*.catchError((error) {
_isLoading = false;
notifyListeners();
return false;
});*/
Future<bool> updateProduct(String title, String description, File image,
double price, LocationData locData) async {
_isLoading = true;
notifyListeners();
String imageUrl = selectedProduct.image;
String imagePath = selectedProduct.imagePath;
if (image != null) {
final uploadData = await uploadImage(image);
if (uploadData == null) {
print('Upload failed!');
return false;
}
imageUrl = uploadData['imageUrl'];
imagePath = uploadData['imagePath'];
}
final Map<String, dynamic> updateData = {
'title': title,
'description': description,
'imageUrl': imageUrl,
'imagePath': imagePath,
'price': price,
'loc_lat': locData.latitude,
'loc_lng': locData.longitude,
'loc_address': locData.address,
'userEmail': selectedProduct.userEmail,
'userId': selectedProduct.userId,
};
try {
await http.put(
'https://flutter-aplikacija.firebaseio.com/products/${selectedProduct.id}.json?auth=${_authenticatedUser.token}',
body: json.encode(updateData));
_isLoading = false;
final Product updatedProduct = Product(
id: selectedProduct.id,
title: title,
description: description,
image: imageUrl,
imagePath: imagePath,
price: price,
location: locData,
userEmail: selectedProduct.userEmail,
userId: selectedProduct.userId);
_products[selectedProductIndex] = updatedProduct;
notifyListeners();
return true;
} catch (error) {
_isLoading = false;
notifyListeners();
return false;
}
}
Future<bool> deleteProduct() {
_isLoading = true;
final deletedProductId = selectedProduct.id;
_products.removeAt(selectedProductIndex);
_selProductId = null;
notifyListeners();
return http
.delete(
'https://flutter-aplikacija.firebaseio.com/products/$deletedProductId.json?auth=${_authenticatedUser.token}')
.then((http.Response response) {
_isLoading = false;
_products.removeAt(selectedProductIndex);
notifyListeners();
return true;
}).catchError((error) {
_isLoading = false;
notifyListeners();
return false;
});
}
Future<Null> fetchProducts({onlyForUser = false, clearExisting = false}) {
_isLoading = true;
if (clearExisting) {
_products = [];
}
notifyListeners();
return http
.get(
'https://flutter-aplikacija.firebaseio.com/products.json?auth=${_authenticatedUser.token}')
.then<Null>((http.Response response) {
final List<Product> fetchedProductList = [];
final Map<String, dynamic> productListData = json.decode(response.body);
if (productListData == null) {
_isLoading = false;
notifyListeners();
return;
}
productListData.forEach((String productId, dynamic productData) {
final Product product = Product(
id: productId,
title: productData['title'],
description: productData['description'],
price: productData['price'],
location: LocationData(
address: productData['loc_address'],
latitude: productData['loc_lat'],
longitude: productData['loc_lng']),
image: productData['imageUrl'],
imagePath: productData['imagePath'],
userEmail: productData['userEmail'],
userId: productData['userId'],
isFavorite: productData['wishlistUsers'] == null
? false
: (productData['wishlistUsers'] as Map<String, dynamic>)
.containsKey(_authenticatedUser.id));
fetchedProductList.add(product);
});
_products = onlyForUser
? fetchedProductList.where((Product product) {
return product.userId == _authenticatedUser.id;
}).toList()
: fetchedProductList;
_isLoading = false;
notifyListeners();
_selProductId = null;
}).catchError((error) {
_isLoading = false;
notifyListeners();
return;
});
}
void toggleProductFavoriteStatus() async {
final bool isCurrentlyFavorite = selectedProduct.isFavorite;
final bool newFavoriteStatus = !isCurrentlyFavorite;
final Product updateProduct = Product(
id: selectedProduct.id,
title: selectedProduct.title,
description: selectedProduct.description,
price: selectedProduct.price,
image: selectedProduct.image,
imagePath: selectedProduct.imagePath,
location: selectedProduct.location,
userEmail: selectedProduct.userEmail,
userId: selectedProduct.userId,
isFavorite: newFavoriteStatus);
_products[selectedProductIndex] = updateProduct;
notifyListeners();
http.Response response;
if (newFavoriteStatus) {
response = await http.put(
'https://flutter-aplikacija.firebaseio.com/products/${selectedProduct.id}/wishlistUsers/${_authenticatedUser.id}.json?auth=${_authenticatedUser.token}',
body: json.encode(true));
} else {
response = await http.delete(
'https://flutter-aplikacija.firebaseio.com/products/${selectedProduct.id}/wishlistUsers/${_authenticatedUser.id}.json?auth=${_authenticatedUser.token}');
}
if (response.statusCode != 200 && response.statusCode != 201) {
final Product updateProduct = Product(
id: selectedProduct.id,
title: selectedProduct.title,
description: selectedProduct.description,
price: selectedProduct.price,
image: selectedProduct.image,
imagePath: selectedProduct.imagePath,
location: selectedProduct.location,
userEmail: selectedProduct.userEmail,
userId: selectedProduct.userId,
isFavorite: !newFavoriteStatus);
_products[selectedProductIndex] = updateProduct;
notifyListeners();
}
_selProductId = null;
}
void selectProduct(String productId) {
_selProductId = productId;
if (productId != null) {
notifyListeners();
}
}
void toggleDisplayMode() {
_showFavorites = !_showFavorites;
notifyListeners();
}
}
mixin UserModel on ConnectedProductsModel {
Timer _authTimer;
PublishSubject<bool> _userSubject = PublishSubject();
User get user {
return _authenticatedUser;
}
PublishSubject<bool> get userSubject {
return _userSubject;
}
Future<Map<String, dynamic>> authenticate(String email, String password,
[AuthMode mode = AuthMode.Login]) async {
_isLoading = true;
notifyListeners();
final Map<String, dynamic> authData = {
'email': email,
'password': password,
'returnSecureToken': true
};
http.Response response;
if (mode == AuthMode.Login) {
response = await http.post(
'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=AIzaSyAxgIztWNLV_0Xs9wqSo7rOosSKThxsaxg',
body: json.encode(authData),
headers: {'Content-Type': 'application/json'},
);
} else {
response = await http.post(
'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=AIzaSyAxgIztWNLV_0Xs9wqSo7rOosSKThxsaxg',
body: json.encode(authData),
headers: {'Content-Type': 'aplication/json'},
);
}
final Map<String, dynamic> responseData = json.decode(response.body);
bool hasError = true;
String message = 'Something went wrong.';
print(responseData);
if (responseData.containsKey('idToken')) {
hasError = false;
message = 'Authentication succeeded!';
_authenticatedUser = User(
id: responseData['localId'],
email: email,
token: responseData['idToken']);
setAuthTimeout(int.parse(responseData['expiresIn']));
_userSubject.add(true);
final DateTime now = DateTime.now();
final DateTime expiryTime =
now.add(Duration(seconds: int.parse(responseData['expiresIn'])));
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('token', responseData['idToken']);
prefs.setString('userEmail', email);
prefs.setString('userId', responseData['localId']);
prefs.setString('expiryTime', expiryTime.toIso8601String());
} else if (responseData['error']['message'] == 'EMAIL_EXISTS') {
message = 'This email already exists.';
} else if (responseData['error']['message'] == 'EMAIL_NOT_FOUND') {
message = 'This email was not found.';
} else if (responseData['error']['message'] == 'INVALID_PASSWORD') {
message = 'The password is invalid.';
}
_isLoading = false;
notifyListeners();
return {'success': !hasError, 'message': message};
}
void autoAuthenticate() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final String token = prefs.getString('token');
final String expiryTimeString = prefs.getString('expiryTime');
if (token != null) {
final DateTime now = DateTime.now();
final parsedExpiryTime = DateTime.parse(expiryTimeString);
if (parsedExpiryTime.isBefore(now)) {
_authenticatedUser = null;
notifyListeners();
return;
}
final String userEmail = prefs.getString('userEmail');
final String userId = prefs.getString('userId');
final int tokenLifespan = parsedExpiryTime.difference(now).inSeconds;
_authenticatedUser = User(id: userId, email: userEmail, token: token);
_userSubject.add(true);
setAuthTimeout(tokenLifespan);
notifyListeners();
}
}
void logout() async {
_authenticatedUser = null;
_authTimer.cancel();
_userSubject.add(false);
_selProductId = null;
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.remove('token');
prefs.remove('userEmail');
prefs.remove('userId');
}
void setAuthTimeout(int time) {
_authTimer = Timer(Duration(seconds: time), logout);
}
}
mixin UtilityModel on ConnectedProductsModel {
bool get isLoading {
return _isLoading;
}
}
If someone can show me some example test`s for this file I think i could work it out on the other files. Thanks.
Here is one of the test`s that i use
import 'package:aplikacija/pages/product_edit.dart';
import 'package:flutter_test/flutter_test.dart';
void main(List<String> args) {
test('empty title returns error string', () {
final result = BuildTitleTextValidator.validate('');
expect(result, 'Title is required and should be 5+ characters long.');
});
test('non-empty title returns null', () {
final result = BuildTitleTextValidator.validate('product');
expect(result, null);
});
test(
'empty dexcription returns error string',
() {
final result = BuildDescriptionTextValidator.validate('');
expect(
result, 'Description is required and should be 10 + character long');
},
);
test(
'non-empty dexcription returns error string',
() {
final result =
BuildDescriptionTextValidator.validate('product description');
expect(result, null);
},
);
test(
'empty price tag returns error string',
() {
final result = BuildPriceTextValidator.validate('');
expect(result, 'Price is required and should be a number');
},
);
test(
'non-empty price tag returns error string',
() {
final result = BuildPriceTextValidator.validate('11');
expect(result, null);
},
);
}
Thank you for your help.