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.
Related
Dart allows to use the standard library names for conditional import/export, like this:
export 'src/hw_none.dart' // Stub implementation
if (dart.library.io) 'src/hw_io.dart' // dart:io implementation
if (dart.library.html) 'src/hw_html.dart'; // dart:html implementation
Is it possible to define a custom property/condition? For example, pass it when compiling.
I have a project that I would like to split into two variants: Flutter variant and pure Dart variant. The choice of the variant depends at compile time, and the necessary implements of abstract classes defines at compile time.
Custom properties where an experimental feature in Dart 1. With Dart 2 there is no longer any support for user defined custom conditions in compile time.
Here is the discussion referencing your question.
All you can do is, switch between implementations on run time:
abstract class SomeFactory {
String get message;
factory SomeFactory() {
if(Platform.environment['SOME_VAR'] != null)
return new _SomeImplementation();
return new _SomeOtherImplementation();
}
}
class _SomeImplementation implements SomeFactory {
#override
String get message => 'SomeImplementation';
}
class _SomeOtherImplementation implements SomeFactory {
#override
String get message => "OtherImplementation";
}
Check this blog entry for more details.
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.
As you see in example,
I have Core class for distribute the shared variables/methods etc. into the mixins.
Abstract class for defining necessary methods, providing summary about api.
Main class for importing everything like a provider.
There isn't any runtime error of course.
Problem with this approach, mixin methods does not recognize #override annotation.
I want to create granular, clean structure for my packages. What is the best approach for this situation or what is the mistake I'm doing?
abstract class AbstractCore {
void foo();
void bar();
}
class Core {
var shared;
}
mixin Feature1 on Core {
#override // not recognized by syntax of course
void foo() {
// something with [shared]
}
}
mixin Feature2 on Core {
#override // not recognized
void bar() {
// yet another thing with [shared]
}
}
class Main with Core, Feature1, Feature2 implements AbstractCore {}
You can accept like:
Core: ApiBase(For sharing Client object, constants, keeping dispose method...)
Feature1: let's say Authentication related Api calls
Feature2: let's say Content related Api calls
Main: Api provider.
Annotations don't have any impact on what the code do. They are just used for readability and tooling.
In your care, it is the analyzer that is complaining about #override, because you're not overriding anything.
Simply remove the #override decorator — it wasn't needed to begin with.
Looking at the source code for the Android Architecture Components sample GithubBrowerSample, I don't understand the point of double injecting the githubApp.
Wouldn't the inject method be enough? Why do it need both of them in the same sentence?
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance Builder application(Application application);
AppComponent build();
}
void inject(GithubApp githubApp);
}
And they use it like:
public static void init(GithubApp githubApp) {
DaggerAppComponent.builder().application(githubApp)
.build().inject(githubApp);
As Thomas Broyer described, you've got two separate directions to set up: You want the Dagger graph to know how to get to your Application instance, and you want to get access to certain bindings out of the dependency graph.
The #BindsInstance line in the Builder creates a binding for Application set to the instance you pass in. It sounds like you understand this part.
However, after you've created your Component, presumably you want to use it. Let's say you want to get fully-injected instances of classes Dep1, Dep2, and Dep3 out of your graph. One way you could do this is to create methods on your Component that get the instances:
#Singleton #Component(/* ... */) interface AppComponent {
// [builder snipped out here]
fun getDep1(): Dep1
fun getDep2(): Dep2
fun getDep3(): Dep3
}
And then you call those as part of your App creation.
var appComponent = DaggerAppComponent.builder().application(githubApp).build()
var dep1 = appComponent.getDep1()
var dep2 = appComponent.getDep2()
var dep3 = appComponent.getDep3()
// Use dep1, dep2, and dep3 here.
However, you can also create a single-arg method, which is typically a void method called inject. This populates all of the #Inject-annotated fields and calls all of the #Inject-annotated methods on the instance you pass in. If GitHubApp has #Inject-annotated-fields (and it does), the call to inject lets you skip defining all of the getters on the Component. That reduces all of the above code to:
DaggerAppComponent.builder().application(githubApp)
.build().inject(githubApp)
...which is what you see in the demo.
The #BindsInstance tells Dagger that it should inject the application into whichever #Inject Application it finds in the dependency graph.
The second asks Dagger to inject dependencies into it's #Inject-annotated fields and methods. This the root of the dependency graph.
Technically, the component method can be called as many times as you like, while the builder method can only be called once.
Is it possible to write a Unit Test that calls the Messenger.Default.Register method and then write an Assertion to be used by the Action?
I would like to determine if my ViewModel is sending the correct message after calling an Execute on one of my Commands.
I have tried writing the Assert.AreEqual as the Action however this doesn't seem to be working correctly.
Sounds like a job for mocking! Assuming you're passing in the messenger interface to your viewmodel (because dependency inversion is a Good Thing, for this very reason), your code should look something like this if I understand you correctly:
public class YourViewModel
{
readonly IMessenger messenger;
public YourViewModel(IMessenger messenger)
{
this.messenger = messenger;
// setup of your delegate command to call Execute
}
void Execute(object parameter)
{
messenger.Send(new YourMessageType());
}
}
Then in your unit test you'd mock the messenger and verify that the right method is called, which in this case is Send. So, using the popular mocking framework Moq:
public class YourViewModelTests
{
[Test]
public void Execute_Always_SendsYourMessageType()
{
// arrange
var mockRepository = new MockRepository(MockBehavior.Loose);
var mockMessenger = mockRepository.Create<IMessenger>();
var systemUnderTest = new YourViewModel(mockMessenger.Object);
// act
systemUnderTest.YourCommand.Execute(null);
// assert
mockMessenger.Verify(p => p.Send<YourMessageType>(
It.Is(m => /* return true if it's the right message */)));
}
}
Usually I'd move the just about all of the "arrange" phase into a test setup method, but you should get the idea.
If you'd still like to do it without mocking the messenger and also use Messenger.Default, you can do the following:
public class YourViewModelTests
{
[Test]
public void Execute_Always_SendsYourMessageType()
{
// arrange
var systemUnderTest = new YourViewModel();
// Set the action to store the message that was sent
YourMessageType actual;
Messenger.Default.Register<YourMessageType>(this, t => actual = t);
// act
systemUnderTest.YourCommand.Execute(null);
// assert
YourMessageType expected = /* set up your expected message */;
Assert.That(actual, Is.EqualTo(expected));
}
}
Alternatively, for each test it is possible to create a separate copy of the Messenger. For the runtime you want to use the Default instance of the Messenger, but for Unit Tests, as I said, create a separate copy for each test:
return new GalaSoft.MvvmLight.Messaging.Messenger(); // Unit Tests
return GalaSoft.MvvmLight.Messaging.Messenger.Default; // Runtime
Otherwise one might end up re-inventing the wheel, since in more complex situations where there is a need to test ViewModel communications, you will have to manage Messenger subscribers, message types an so on. Then probably writing unit tests for the messenger mock making sure it works in the same way as the original messenger. There is nothing in the engine of the Messenger that should be different when comparing Runtime and Test executions.
So for testing a factory returns the same instance of the Messenger. Test method subscribes and waits, ViewModel publishes; then Test accepts and exits. Otherwise Test times out and reports an error. I found this approach more "closer to reality" than mocking the messenger and verifying through the mock that the method was called.