Unit Testing hive abstraction layer - flutter

So I created a simpler level of abstraction to use Hive into my Flutter app. This should be the central point, where all hive boxes are administrated and accessed. Since e.g. getApplicationDocumentsDirectory is not available during testing, how can I still manage to test this whole file?
import '../services/workout.dart';
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart' as path_rovider;
import 'workout.dart';
class HiveService {
static final HiveService _singleton = HiveService._internal();
static const String _workoutBox = "workoutBox";
factory HiveService() {
return _singleton;
}
HiveService._internal();
static Future<void> init() async {
final appDocumentDirectory =
await path_rovider.getApplicationDocumentsDirectory();
Hive.init(appDocumentDirectory.path);
Hive.registerAdapter(WorkoutAdapter());
}
static Future openWorkouts() {
return Hive.openBox<Workout>(_workoutBox);
}
static Future close() {
return Hive.close();
}
}

First thing first, you need to initial Hive at the top of the main method in your test file, and then you can proceed with the rest of the tests.
You can use it in this way:
void initHive() {
var path = Directory.current.path;
Hive.init(path + '/test/hive_testing_path');
}
main() {
initHive();
//The rest of your test code.
}

Related

getting an error of register adapter in flutter using hive

I have created a simple app for storing data using hive and getx as state management
I have followed all the steps but even thought its showing an error like
After coding when I played ...it worked fine...but when I restart the app...it stops showing white background and an error in console....
have u forgotten register adapter...
I have registered adapter in main...eventhough getting this error
error is
Unhandled Exception: HiveError: Cannot read, unknown typeId: 32. Did you forget to register an adapter?
class TransactionController extends GetxController
{
List<TransactionModel> _transactions=[];
Box<TransactionModel> transactionbox=Hive.box('a');
List<TransactionModel> get transactions{
return _transactions;
}
TransactionController(){
_transactions=[];
for(int x=0;x<transactionbox.values.length;x++)
{
var trns=transactionbox.getAt(x);
if(trns!=null)
_transactions.add(trns);
}
}
int get countlength{
return _transactions.length;
}
void addTransaction(TransactionModel transaction)
{
_transactions.add(transaction);
transactionbox.add(transaction);
update();
}
}
this is model class
import 'package:hive_flutter/hive_flutter.dart';
import 'package:hive/hive.dart';
part 'transactionmodel.g.dart';
#HiveType(typeId: 0)
class TransactionModel
{
#HiveField(0)
String id;
#HiveField(1)
String detail;
#HiveField(2)
bool isExpense;
TransactionModel({
required this.id,
required this.detail,
required this.isExpense,
});
}
this is void main
void main() async
{
WidgetsFlutterBinding.ensureInitialized();
Directory dir=await getApplicationDocumentsDirectory();
await Hive.initFlutter(dir.path);
await Hive.openBox<TransactionModel>('a');
Hive.registerAdapter<TransactionModel>(TransactionModelAdapter());
runApp(MyApp());
}
I see a wrong thing in your code, you're trying to open the Hive box before registering the adapter, you're calling Hive.registerAdapter after you already opened the box, change it with this:
void main() async
{
WidgetsFlutterBinding.ensureInitialized();
Directory dir=await getApplicationDocumentsDirectory();
await Hive.initFlutter(dir.path);
Hive.registerAdapter<TransactionModel>(TransactionModelAdapter()); // this should be first
await Hive.openBox<TransactionModel>('a'); // then this
runApp(MyApp());
}

How can i build async method from scratch?

I want to convert sync method to run as asynchronous
Simple example :
Future<void> increment() async {
for (var i = 0; i < 100000000; i++) {
_counter++;
}
}
When I use this code with flutter the app will freeze, because the code content is running as sync, so now I want to know how can I make this code run as async?
i tried to add Future.delayed as following :
Future<void> increment() async {
for (var i = 0; i < 100000000; i++) {
_counter++;
await Future.delayed(const Duration(microseconds: 1));
}
}
But in some scenarios, it will takes too long time!
Is there a better solution?
Use Isolates in Dart for heavy calculations
There is compute constant in Flutter api for top-level and static functions who manage with Isolates by themselves.
Look into this page Concurrency in Dart
Paste the following code into a test.dart file, and see how you can create an async method from scratch using isolates.
import 'dart:io';
import 'dart:isolate';
void main() async {
// Read some data.
final result = await Isolate.run(_readAndParseDart);
print("Called the async function");
}
String _readAndParseDart() {
final fileData = File("test.dart").readAsStringSync();
print('Finished the asynchronous code');
return fileData;
}
Also try this code and notice the difference (which result will be printed first) when we do not use the async/await when calling the asynchronous method:
import 'dart:io';
import 'dart:isolate';
void main() {
// Read some data.
final result = Isolate.run(_readAndParseDart);
print("Called the async function");
}
String _readAndParseDart() {
final fileData = File("test.dart").readAsStringSync();
print('Finished the asynchronous code');
return fileData;
}

The static method can't be acessed through an instance. Try using the class 'services' to acess the method

Hi can anyone help me with this problem I'm facing when calling API's in flutter, this is the code for fetching the data
class _InvestPageState extends State<InvestPage> {
late Future<Markets> _Markets;
#override
void initState() {
_Markets = Services().getMarkets(); //error here
super.initState();
}
This is the code in my API manager file
import 'package:gem_portal_new/Login/newsinfo.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class Services {
static const String url = 'https://ctrade.co.zw/mobileapi/MarketWatch';
static Future<List<Markets>> getMarkets() async {
try {
final response = await http.get(Uri.parse(url));
if (200 == response.statusCode) {
final List<Markets> markets = marketsFromJson(response.body);
return markets;
} else {
return <Markets>[];
}
} catch (e) {
return <Markets>[];
}
}
}
You are trying to access a static method using a object instance,
Change this
_Markets = Services().getMarkets();
to
_Markets = Services.getMarkets();
Try this
class _InvestPageState extends State<InvestPage> {
late Future<Markets> _Markets;
#override
void initState() {
Services().getMarkets().then((value) {
_Markets = value;
});
super.initState();
}
}
You are used future return type, so you cannot be access through instance.

how to implement Shared_preferences in my own class elegantly on Flutter?

I created such class to store and update the api address locally. But it doesn't work well. How to add the Shared_preferences in the normal class instead of the flutter state widget? So it would make things clearly.
// The Server class
import 'package:shared_preferences/shared_preferences.dart';
class Server{
String _listUrl;
String _itemUrl;
static String _cache1;
static String _cache2;
static final Server _server = new Server._internal();
factory Server({String listUrl, String itemUrl}) {
_cache1 = listUrl;
_cache2 = itemUrl;
return _server;
}
Server._internal() {
read();
_listUrl=_cache1??"https://www.sjjg.uk./eat/food-items/";
_itemUrl=_cache2??"https://www.sjjg.uk/eat/recipe-details/";
}
String listUrl()=>_listUrl;
String itemUrl()=>_listUrl;
void update({String listUrl, String itemUrl}){
_listUrl = listUrl??_listUrl;
_itemUrl = itemUrl??_itemUrl;
save();
}
void read() async{
SharedPreferences prefs = await SharedPreferences.getInstance();
_cache1=prefs.getString('_listUrl')??"https://www.sjjg.uk./eat/food-items/";
_cache2=prefs.getString('_itemUrl')??"https://www.sjjg.uk/eat/recipe-details/";
// print(_cache1);
// print(_cache1);
}
void save() async{
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('_listUrl', _listUrl);
prefs.setString('_itemUrl', _itemUrl);
}
}
I found the problem.
I should not import 'package:flutter_test_app/server.dart';
Still difference between it with import 'server.dart';

Rx Flutter Request List From JsonPalceHolder

I try to get List from jsonPlaceHolder using flutter rxdart stream and try to apply bloc pattern on it.
this class that response for get post response from api
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/post_item.dart';
class ItemApi {
Future<List<JsonItem>> getPost() async {
String _url = 'https://jsonplaceholder.typicode.com/posts';
final _response = await http.get(_url);
if (_response.statusCode == 200) {
return (json.decode(_response.body) as List)
.map((jsonItem) => JsonItem.fromJson(jsonItem))
.toList();
}
}
}
I using repository class to wrap using ItemApi class
import 'json_item_request.dart';
import '../models/post_item.dart';
class Repository{
final jsonItemResponse = ItemApi();
Future<List<JsonItem>> getItem() => jsonItemResponse.getPost();
}
at the last i using bloc class that response for get data and set it inside PublishSubject
import '../models/post_item.dart';
import '../resouces/repository.dart';
import 'package:rxdart/rxdart.dart';
class JsonBloc {
final _repository = Repository();
final _streamOfJsonList = PublishSubject<List<JsonItem>>();
Observable<List<JsonItem>> get jsonList=> _streamOfJsonList.stream;
fetchAllPost() async{
Future<List<JsonItem>> list = _repository.getItem();
}
dispose(){
_streamOfJsonList.close();
}
}
My question is how i can set response inside _streamOfJsonList variable to using it when list changed.
Sounds like you already have all the moving parts connected? If so you just need to add the item list to the PublishSubject:
void fetchAllPost() async {
List<JsonItem> list = await _repository.getItem();
_streamOfJsonList.add(list);
}
This will trigger the onListen callback with the new list on anything that is listening to the stream.
You can add error and data to ReplaySubject like below :
void fetchAllPost() async {
List<JsonItem> list = await _repository.getItem();
if (list != null) {
_streamOfJsonList.sink.add(list);
} else {
_streamOfJsonList.addError("ERROR");
}
}