Testing Flutter code that uses a plugin and platform channel - dart

I have a flutter plugin which uses the platform channel to do some native work.
How do I properly write tests for my application that requires this plugin?
Unit tests only are good for pure dart functions. I don't believe Widget testing will be able to test things that use the platform channel to native. So that leaves integration testing.
From what I understand is that integration testing will start your main application and you can control it around your app and test things.
For my case, I want to test just the code that uses the plugin (that uses the platform channel for native stuff).
Also what is important is the values that come back from the platform channel, so it is important to call the native side using a real platform channel and not a mock one.
Is that possible? Can I tell the integration tester to open a dummy version of my application, kind of like an integrated widget tester?

It seems the short answer to your question is no. Flutter driver (integration testing) can only interact with the UI, AFAIK. It cannot intercept calls to plugins. It is used to test the entire app from the UI.
However it is possible to intercept calls to plugins in unit and widget tests. This allows monitoring the calls to the plugin and mocking the response. That way you can test your plugin's dart code and/or a widget that uses the plugin. Testing native code would involve writing native tests.
The following is an example of intercepting calls to a plugin for testing:
MethodChannel('audio_recorder')
.setMockMethodCallHandler((MethodCall methodCall) async {
log.add(methodCall);
switch (methodCall.method) {
case 'start':
isRecording = true;
return null;
case 'stop':
isRecording = false;
return {
'duration': duration,
'path': path,
'audioOutputFormat': extension,
};
case 'isRecording':
return isRecording;
case 'hasPermissions':
return true;
default:
return null;
}
});
For a complete example see here

Flutter team do mentions that they want to do more things in widgets test instead of driver(integration) test.
We're moving away from flutter_driver in favour of extending flutter_test to work on devices.
From Flutter 2.8 breaking changes doc, you should use binding in tester instead of setMockMethodCallHandler from channel.
// old code
myMethodChannel.setMockMethodCallHandler(...);
myMethodChannel.checkMockMethodCallHandler(...);
// new code
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(myMethodChannel, ...);
tester.binding.defaultBinaryMessenger.checkMockMessageHandler(myMethodChannel, ...);
Take ImagePicker as example:
widgetTest('', (tester) async {
const channel = MethodChannel('plugins.flutter.io/image_picker');
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
channel,
(MethodCall methodCall) => Future.value('some-image'),
);
});

One additional thing to #呂學洲 answer.. is if you're writing a unit test and don't have reference to widget tester instance you can access the binder like below
TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger
Also make sure to wait for binder to initialize in the begging of the test like below
TestWidgetsFlutterBinding.ensureInitialized();
And to grab method call you can do:
final binaryBinding = TestDefaultBinaryMessengerBinding.instance;
pluginChannel = MethodChannel('channelName');
binaryBinding!.defaultBinaryMessenger.setMockMethodCallHandler(pluginChannel,
(MethodCall methodCall) async {
switch (methodCall.method) {
case 'call 1':
return mockValue;
default:
return null;
}
},
);
Hope that helps.

Related

Set application version in SCDF from Java DSL

I'm trying to deploy a stream using Java DSL.
Here the code that creates stream (fluent style)
private void create() {
builder
.name(this.STREAM_NAME)
.source(source)
.sink(sink)
.create();
}
deployment method
private void deploy() {
dataFlowOperations.streamOperations().deploy(this.STREAM_NAME, new HashMap());
waitForStatus("deployed");
}
and a method that creates a bean for source application
#Bean
public StreamApplication source() {
return new StreamApplication("httpsource")
.addProperty("app.httpsource.version", "0.0.17")
.addProperty("spring.cloud.dataflow.httpsource.validation.schema", this.properties.getHttpSource().getJsonSchema());
}
I first trigger create method, then deploy. The problem is - I want to set application version to 0.0.17 for httpsource application, but final pod that is deployed for this app has image version equal to default version 0.0.1. Is it possible to set application version with Java DSL?
When deploying streams/tasks, optionally, you can pass in the version parameters as part of the deployment properties. Specifically, for versions, you'd pass them with the version.<APP_NAME> convention.
The DataflowIT presents such test combinations (see: sample); feel free to use it for reference. We rely on these tests for IT and as well as end-to-end Acceptance Tests.

Dart Keyboard Event Command Line

I've been monkeying around with Dart (& Flutter more specifically on mobile) and have become quite interested in trying Flutter on Desktop.
Anyways, for this one app idea, I need the ability to create a key event. From my research, I found this: https://api.dartlang.org/stable/2.2.0/dart-html/KeyEvent-class.html which mentions a KeyEvent however this primarily relates to Dart:HTML (which I presume just means browser only).
Does Dart run in the command line support any ability for generating key events? Like say I wanted an app to type something for a user.
Thanks!
#Isaac has basically explained it in his comment but this is how it looks like in code:
import 'dart:io';
void main(){
stdin.echoMode = false;
stdin.lineMode = false;
while(true){
if(stdin.readByteSync() == 102){ // f
print('You payed respect');
}
else{break;}
}
}

How can I implement a custom challenge handler in Xamarin for iOS using IBM MobileFirst?

I'm trying to implement an adapter-based authentication using IBM MobileFirst Platform Foundation 6.3 and Xamarin in iOS.
I have followed the IBM documentation on how to setup a customSecurityTest, adding realms and equivalent loginModules within authenticationConfig.xml. I have then setup 2 adapter procedures:
authenticateUser with securityTest="wl_unprotected", and another
HelloFromServer with a securityTest="SingleStepAuthAdapter" that actually does a user authentication, and executing WL.Server.setActiveUser("SingleStepAuthRealm", userIdentity) to create the user identity.
I have then created an iOS app using Xamarin Studio. Tried to invoke HelloFromServer, which as expected runs my ChallengeHandler module BUT within the HandleChallenge method while trying to invoke the authenticateUser procedure on the server, it respond back with another authRequired=TRUE.
Anybody having the same problem?
You did not provide any useful implementation code that can be inspected for errors - add both the adapter authentication implementation as well as the client-side code of the challenge handler.
While I have no experience with Xamarin, it should be noted that in order to get started you could:
Use the same exact implementation as done in the Adapter-based authentication tutorial. That is, the implementation in the adapter files, project configuration and so on.
Follow through the native iOS implementation, also based on the tutorial
The MFP Studio project and the native iOS project can be downloaded from here.
If you are repeatedly getting back authRequired=true, it looks like you are not notifying the server of the success from your HandleChallenge function. You can refer to the CustomChallengeHandler.cs provided with the sample that is shipped with the component. This is coded to handle a form-based challenge. You can modify it to handle an realm for adapter-based authentication.
So, here are the changes you need to do
1) You should implement the GetAdapterAuthenticationParameters method in your ChallengeHandler class.
For example,
public AdapterAuthenticationInfo AdapterAuthenticationParameters = new AdapterAuthenticationInfo();
....
public override AdapterAuthenticationInfo GetAdapterAuthenticationParameters ()
{
return AdapterAuthenticationParameters;
}
2) In the HandleChallenge function of your ChallengeHandler class, set the isAuthRequired = true. For example,
if (challenge.ResponseJSON["authRequired"] == true)
{
WorklightProcedureInvocationData invocationData = new WorklightProcedureInvocationData("DemoAdapter",
"submitAuthentication" , new object[1]); // Add the parameters you want to pass to the adapter
AdapterAuthenticationParameters.InvocationData = invocationData;
AdapterAuthenticationParameters.RequestOptions = null;
isAdapterAuth = true;
}
else
{
isAdapterAuth = false;
authSuccess = true;
}

Inject bridge-code in JavaFX WebView before page-load?

I want to load some content or page in a JavaFX WebView and offer a Bridge object to Java so the content of the page can do calls into java.
The basic concept of how to do this is described here: https://blogs.oracle.com/javafx/entry/communicating_between_javascript_and_javafx
Now my question is: When is a good time inject the bridge-object into the WebView so it is available as soon as possible.
One option would be after page load as described here: https://stackoverflow.com/a/17612361/1520422
But is there a way to inject this sooner (before the page content itself is initialized), so the bridge-object is available DURING page-load (and not only after page-load)?
Since no one has answered, I'll tell you how I'm doing it, although it is ugly. This provides the ability for the page to function normally in non-Java environments but receive a Java object in Java environments.
I start by providing an onStatusChanged handler to the WebEngine. It listens for a magic value for window.status. If the magic value is received, the handler installs the Java object. (In my case, it's more complex, because I have some more complex orchestration: I'm executing a script that provides a client-side API for the page and then sets another magic value on window.status to cause the Java object to be sent to an initialization method of the client-side API).
Then in my target page, I have the following code in the first script in the page:
window.status = "MY-MAGIC-VALUE";
window.status = "";
This code is essentially a no-op in a "normal" browser but triggers the initialization when running in the custom JavaFX embedding.
In Java 8, you can trigger event changing from SCHEDULED to RUNNING to inject objects at this time. The objects will present in WebEngine before JavaScript running. Java 7, I see the state machine quite differs in operating, no solution given for Java 7.
webEngine.getLoadWorker().stateProperty().addListener(
new ChangeListener<State>(){
public void changed(ObservableValue<? extends State> ov,
State oldState,
State newState)
{
// System.out.println("old: "+oldState+", new: "+newState);
if(newState == State.RUNNING &&
oldState == State.SCHEDULED){
JSObject window = (JSObject)webEngine.executeScript("window");
window.setMember("foutput", foutput);
}
}
});

QTP 11 Extensibility developing (.NET sdk) - calling Record() on the recorder object fails

As far as I looked, there's no answered question about QTP's Ext. sdk on stackoverflow (and almost anywhere else on the net; there isn't even a appropriated tag for it...), so I'm aware it's unlikely I get my problem solved by asking , but whatever, it worth trying.
Anyway, before I lose the attention of anyone who never heard or used the Ext. sdk, maybe I will have more luck asking you to help me figure out how to locate the error log file QTP produces at run-time. I know such a file exists in the new UFT 11.5 version, but I couldn't locate it in QTP 10 or 11 (For the record, I don't talk about QTP's Log Tracking feature, but about the "meta" error log of errors/exceptions produced by QTP itself).
Now for the question:
I'm developing an extension for QTP to support native record and run tests on my application.
I'm currently able to import an object repository, and write test steps using The COM object testing agent I developed.
Problem started when I was trying to implement the IRecordable interface; I'm getting the IRecorder object from qtp, and even able to use it as ISuppressor object to exclude redundant steps from being recorded, but all my attempts to record a step (that is, to add new recorded objects to the repository, and add steps to the test) simply failed.
This is the code that I'm using:
public class MyTestingAgent :
AutInterface.ITestable,
AutInterface.IRecordable
{
QTPInterface.IRecorder recorder;
...
public void AutInterface.IRecordable.BeginRecording(object recorder)
{
IRecordSuppressor recordSuppressor = recorder as IRecordSuppressor;
recordSuppressor.Suppress(MyTestingAgentId,
"<Suppress><Item type=\"HWND\" value=\"[#HWND]\" /></Suppress>".Replace("[#HWND]", getMyAppHWND().ToString()));
this.recorder = recorder as QTPInterface.IRecorder;
...
}
public void recordNewObjStep(string parentName, string objName, string method, Object[] arguments)
{
object[] objectHyrarchy = new object[] { findObjectId(objName), findObjectId(parentName) };
string externalParent = null;
string appDescriptionXml = getDescriptionXml(parentName, objName);
try
{
recorder.Record(MyTestingAgentId, objectHyrarchy , appDescriptionXml, externalParent, method, arguments);
Trace.TraceInformation("Record successfully done.");
}
catch (Exception e)
{
Trace.TraceError("TEAAgent.recordSTElement: " + e.ToString());
}
}
...
}
I'm pretty sure all the arguments I send with the call to Record() are accurate. getDescriptionXml() and findObjectId() are used in different cases in the code, and works fine, the method name and argument are correct.
The annoying thing is that the call to Record doesn't even throw exception, and I get "Record successfully done." in the trace log. Needless to say no new object is created in the repository, and no step is added to the test.
As I can't debug QTP, I'm pretty much in the dark with what I'm doing wrong. That's why I'm asking for help with finding QTP's log file, or any other approach that might shed some light on the subject.
For QTP 11 you can turn on the logs by going to QTP's bin directory and running ClientLogs.exe.
Specifically for TEA extensibility do the following.
select the QTP node from the list on the left
find the LogCatPackTEA from the Available Categories list
Click the > button to move it to Selected Categories
Change TEAs level to Debug2 by selecting the category and changing the level
Click OK and run QTP
The logs will show up as QTP.log in the diretory specified in Path:
I'm curious on what the problem you're facing is, please update if you find the cause.

Resources