How to mock database in flutter - flutter

I tried to mock database to test my local api, i search in official document finding mockito which can work with remote api fine, but also can not work with local database out of box, is there any way to work around of it?

In these cases, you have two options (among many others). Even if my examples assume you're making HTTP calls, it doesn't matter. You can use these strategies regardless the specific use case I'm exposing!
The first one is using the "Strategy pattern" to create an interface for the API and then switch between a test and a production API. Here's a simple example:
abstract class HttpRepository {
const HttpRepository();
Future<Something> sendRequest();
}
You can now create 2 concrete classes: one is for the actual API call and the other is just a mock for tests.
/// Use this in your tests
class MockHttpRepository extends HttpRepository {
const MockHttpRepository();
#override
Future<Something> sendRequest() async {
// Simulating the HTTP call
await Future.delayed(const Duration(seconds: 2));
return Something();
}
}
/// Use this in your Flutter code to make the actual HTTP call or whatever else
class ApiHttpRepository extends HttpRepository {
const ApiHttpRepository();
#override
Future<Something> sendRequest() async {
// Doing a real HTTP call
final response = await makeGetOrPost();
return Something.withData(response);
}
}
In this way, you'll use ApiHttpRepository in your Flutter app and MockHttpRepository in tests. Use const constructors whenever possible.
The other way is using mocks to simulate fake HTTP calls or anything else. Basically, you're using when to "trap" a method call and return a fake response you can control.
// 1. "Enable" mocking on your type
class MockRepo extends Mock implements ApiHttpRepository {}
// 2. Mock methods
const ApiHttpRepository repo = MockRepo();
when(repo.sendRequest()).thenAnswer((_) async => Something());
In this case, we're using thenAnswer because the return type of sendRequest() is of type Future<T>. In your case, if you are reading data from a database you just need to:
Make your class "mockable" using extends Mock implements YourClass
Use when on the mockable instance and control the output
Make sure to use thenAnswer if the method returns a Future<T> and thenReturn in all the other cases.

Related

Making an API accessible throughout all widgets - use Provider with already instantiated variable?

I wrote an API class that, obviously, encapsulates a couple http requests. However, this API class also stores a little bit of state: Namely, it saves an authentication token that will be used in all subsequent requests after the first.
In my flutter function, I then first wait for that token to be set before doing anything else:
Future<void> main() async {
var api = MyAPI();
await api.auth.refreshTokens(); // Sets api.auth.token
runApp(MaterialApp(etc));
What is important here is that in etc I am also setting Providers, one of which being that very API:
providers: [Provider(create: (context) => api), ...]
which I then want to use all throughout my app. However, is that really the best way to go about it? It seems like a really really cumbersome and messy approach to simply be able to use this more or less global API object.
How is it usually done?
You can write that class as a singleton, this way you can call the constructor and obtaining each time the same instance without creating a new one:
void main() {
Api().call();
Api().call();
}
class Api {
static Api? _instance;
Api._();
factory Api() {
_instance ??= Api._();
return _instance!;
}
String token = '';
void call() {
token += '*';
print('new token $token');
}
}

How do I initialize a long-lived class in a Flutter app?

I have an Api class that accepts an (optional) authentication token that it uses for making authenticated requests e.g. Api(token: 'a9sa2ksas12').getUserDetails().
If the token is not passed, it has to perform the relatively expensive operation of reading it from sharedPreferences.
class Api {
static const BASEURL = "https://api.google.com/";
final String token;
Api({ this.token });
Future<http.response> getUserDetails() async {
return http.get('$BASEURL/user/', headers: { 'Authorization': token });
}
}
How can I setup my app so that the token is read only once from sharedPreferences and used throughout the app for all future Api() requests?
Some ideas I've considered and think that may work:
Have token be a global variable
Make the API class a singleton, and pass it around between "screens"
Well in general there's nothing bad in making you Repositories a singletons. But on the other hand, I don't like the concept of passing the API classs between the screens.
When widgets use your data source directly without any middleman, like Bloc or Provider, they tend to be polluted with a lot of presentation logic. I personally prefer to separate those concerns and let widgets do what they are made for: rendering the UI. This makes them smaller and easier to test.
What's more the API class should be responsible only for the network calls. It shouldn't be responsible for managing the token. I'd inject to the API class something like:
class TokenProvider
{
Future<String> getToken();
}
The responsibility of this class would be, you guessed it, to provide a token. It can return cached value or get it from the SharedPreferences. Thanks to this the API doesn't have to care where the token comes and how to handle it. It will do just one thing: api calls.
I ended up using the "Locator" pattern, via get_it.
The code was pretty simple.
Step 1: Setup a top-level locator.dart in lib/
// ./lib/locator.dart
import 'package:my_app/services/api.dart';
import 'package:get_it/get_it.dart';
GetIt locator = GetIt.instance;
void setupLocator() {
locator.registerLazySingleton(() => Api());
}
Step 2: Use api anywhere in the app by just importing the locator:
// ./lib/widgets/some_screen.dart
class _SomeScreenState extends State<SomeScreen> {
Api api = locator<Api>();
#override
void initState() {
api.getUserDetails().then((response) => {
// do anything you like with the response
});
super.initState();
}
The beauty of this approach is that Api is only ever initialized ONCE in the lifetime of the app, so I simply assign a token to it on initState without worrying about the widget getting disposed/rebuilt and repeated fetches to SharedPreferences.

How can I use then() block with RestAssured while using POJO classes?

While working on RestAssured I came across the concept of Serialization and DeSerialization(POJO Classes) to read and validate the response. I went through some tutorial and was able to create the POJO class based on my response.
However, when I use the POJO class reference in my Tests I am not able to use the then() block for different assertions. Below details might clear things bit more :
TestMethod without POJO :
public void listUsers() {
RestAssured.baseURI="https://reqres.in/";
Response res = RestAssured.given()
.contentType("application/json")
.queryParam("page", 2)
.when()
.get("/api/users")
.then()
.assertThat().statusCode(200).and()
.body("page", Matchers.equalTo(2)).and()
.body("total", Matchers.greaterThanOrEqualTo(1))
.body("data.email", Matchers.hasItem("george.edwards#reqres.in"))
.extract().response();
JsonPath jsonpath = new JsonPath(res.asString());
System.out.println(jsonpath.get("data[0].email"));
}
Test Method with POJO :
public void listUserswithPOJO() {
RestAssured.baseURI="https://reqres.in/";
ListUsers res = RestAssured.given()
.contentType("application/json")
.queryParam("page", 2)
.when()
.get("/api/users").as(ListUsers.class);
System.out.println(res.getData().get(1).getEmail());
}
Test Class :
#Test
public void listUsersTest() {
ReqResApi TS1 = new ReqResApi();
TS1.listUserswithPOJO();
}
I want to keep the assertions of the then block as it is while using POJO classes as well. When I try to do so after as(ListUser.class), it gives the compilation error that then() is undefined for ListUser class.
Is there any way in which I can use both POJO class as well as then() block in my rest assured tests.
This is not possible because Return types of these options are different.
MainPojo m1 =RestAssured.given().contentType("application/json").queryParam("page", 2).when().get("/api/users")
.as(MainPojo.class)==> Return Type is ur Class, in this example Main Pojo
System.out.println(m1.getData().get(0).getFirst_name());
RestAssured.given().contentType("application/json").queryParam("page", 2).when()
.get("/api/users").then().assertThat().statusCode(200).and().body("page", Matchers.equalTo(2)).and()
.body("total", Matchers.greaterThanOrEqualTo(1))
.body("data.email", Matchers.hasItem("george.edwards#reqres.in")).extract().response();---> Return Type is Response

How to mock dispatch methods in ReduxAction from flutter package `async_redux`

I am developing an android / ios application in flutter, and I have chosen to use redux for my state management.
I am writing unit tests for my redux actions, which have been implemented using the async_redux package.
I am following the excellent guidelines set out for testing by the author of the package, but I am not sure how to mock the dispatch of further actions from my action under test.
For example, the below LogoutAction dispatchs a DeleteDatabaseAction and waits for it to complete:
class LogoutAction extends ReduxAction<AppState> {
#override
Future<AppState> reduce() async {
await dispatchFuture(DeleteDatabaseAction());
return AppState.initialState();
}
}
class DeleteDatabaseAction extends ReduxAction<AppState> {
#override
FutureOr<AppState> reduce() {
throw StateError(
'Unwanted call to runtime implementation of DeleteDatabaseAction',
);
}
}
void main() {
final store = Store<AppState>(initialState: AppState(loggedIn: true));
final storeTester = StoreTester.from(store);
test('Logout action should return correct state and not throw StateError', () async {
storeTester.dispatch(LogoutAction());
TestInfo<AppState> info = await storeTester.wait(LogoutAction);
expect(info.state.loggedIn, false);
});
}
I want to test only the action under test, and stub out all further action calls.
i.e. How can I mock / stub the dispatch and dispatchFuture methods on ReduxAction, so that the runtime DeleteDatabaseAction implementation is not run?
So far I have attempted:
Inject DeleteDatabaseAction using get_it and inject a mock during test
I have 100+ actions that will now need to be added to my context
Some actions have parameters that change based on where they are called from, so cannot be registered at app startup
Subclass Store, override the above methods and use the subclass in my test here final store = Store<AppState>(initialState: AppState(loggedIn: true))
I will not be able to dispatch my action under test, as it uses the same store in the async_redux test implementation
Here: storeTester.dispatch(LogoutAction());
Create a separate Dispatcher implementation, inject this and override with a mock during tests
This will work, but it is new framework, I can go this route but now I am deviating from the well documented framework provided by asyn_redux
This wasn't available when you asked this question. But now the answer is here: https://pub.dev/packages/async_redux#mocking-actions-and-reducers
To mock an action and its reducer, start by creating a MockStore in your tests, instead of a regular Store. The MockStore has a mocks parameter which is a map where the keys are action types, and the values are the mocks. For example:
var store = MockStore<AppState>(
initialState: initialState,
mocks: {
MyAction1 : ...
MyAction2 : ...
...
},
);
However, there are other ways:
As you said, use get_it or some other dependency injection code to inject the http client into the action reducer. This works well.
Use a DAO. For example:
class DeleteDatabaseAction extends ReduxAction<AppState> {
#override
Future<AppState> reduce() {
await dao.deleteDatabase();
return null;
}
Then you can mock the DAO itself, or inject the DAO via get_it. You can also make the dao a getter to some BaseAction (that extends ReduxAction) and inject it there.

Dart Multiple Annotations & source_gen

I'm trying to create a package for Flutter that provides source generation using source_gen. I would like to be able to annotate a class and fields to identify what needs to be generated. (An example of this would be the libraries Dagger2 or ROOM, for java.)
Given an abstract class:
#ServiceCalls("http://www.whocares.com")
abstract class ServiceCalls
#Get
int getCount();
#Post
void postCount(int count);
}
The following concrete implementation would be generated:
class ServiceCallsImp extends ServiceCalls {
Future<int> getCount() {
// details for implementing a get service call
}
Future<void> postCount(int count) {
// details for implementing a post call
}
}
So, the big questions I'm trying to answer are:
1) Is an abstract class the way to go, or is a part the correct approach?
2) How do I setup builders for a 'recursive' annotation processing? (Annotated fields in an annotated class)
NOTE: I don't really care about service calls, its just an example.