Testing library - should I explicitly use expect? - testing-library

Within the testing library framework, you can write a test like this where the expectation is implied, i.e. the test will fail as a result of the getByText method throwing if it can't find the given text:
it('sets some text', () => {
...
screen.getByText('someText');
});
Although this works, this is relying on the implementation detail of testing library. So, I'm curious to know if this is accepted, or whether an explicit expect should be in place, like
it('sets some text', () => {
...
expect(screen.getByText('someText')).toBeInTheDocument();
});

Related

React Native IOS Bridge Native View - proper way to call Native Method from JS side

I want to discuss the options i have come across for calling a native method from a IOS ViewManager/View in JS side without using properties but direct method calls.
Option A:
implementing a method in the ViewManager which searches for the correct view and calls the given method in the view, like this:
func callMethodViaManager(_ node:NSNumber) {
DispatchQueue.main.async {
let myView = self.bridge.uiManager.view(forReactTag: node) as! MyView
myView.myMethod()
}
}
and then in the JS side implement a handler like this:
const handleSomething = (e) => {
UIManager.dispatchViewManagerCommand(
ReactNative.findNodeHandle(ref.current),
UIManager.SwiftComponent.Commands.callMethodViaManager,
[])
};
This is just a short summary of the relevant parts, the process in full can be seen in full detail maybe just a bit old but with some adjustments one can get it to work also with functional components:
https://medium.com/#jjdanek/react-native-calling-class-methods-on-native-swift-views-521faf44f3dc
Option B:
For this option let's go with the best scenario which is that one can get all the necessary data, setup, etc on the ViewManager ready via delegates for example or some other pattern or Swift sugar.
Calling the Native methods in the ViewManager from the JS side directly with the use of NativeModules, like this:
const handleSomething = (e) => {
NativeModules.MyViewManager.myMethod()
};
I could not find much about this option in correlation to a Native View being bridged, this way of doing it is used for Native Module bridging explicitly. The only things i could find where:
React Native UI Component Method
or guides like these one:
https://teabreak.e-spres-oh.com/swift-in-react-native-the-ultimate-guide-part-1-modules-9bb8d054db03#4377
I have tried both methods and at first glance they seem to work both.
So my questions are:
Why is Option A the better solution/option and the one which is recommended or most used?
What is wrong or can go wrong with Option B?
Is there anything else to take into consideration?
Is there a better way which is not trough properties?
Option B is more flexible.
If you use Option A, then you can't pass Promise and Callback parameter to the native side.
It seems possible in iOS But not in Android.
This is a related issue.
React Native bridge API is all confusing
There is no guide about how to call the native UI method with Promise or Callback.
There is a example about how to pass Promise to native side with calling native UI instance method.
SketchView... is just my example module name.
class SketchViewModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
override fun getName() = "SketchViewModule"
private fun runCommandWithPromise(
viewId: Int, promise: Promise, handler: (view: SketchView) -> Unit
) {
reactApplicationContext.getNativeModule(UIManagerModule::class.java)?.let { uiManager ->
uiManager.addUIBlock {
(it.resolveView(viewId) as? SketchView)?.let { view ->
handler(view)
} ?: run {
promise.safeReject("[runCommandWithPromise] Cannot find View with UIManager")
}
}
}
}
...

Unit Testing Exceptions in Dart/Flutter

I'm trying to unit test using the given code.
test('Given Employee When employeeName less than 5 then throws Exception', () async {
final employee = EmployeesCompanion.insert(
employeeName: 'Tony',
employeeCode: 'HR-121',
address: '5th Floor, Park Avenue',
contact: '1234567890',
hiringDate: DateTime.now());
expectLater(await employeesDB.createEmployee(employee),
throwsA((f) => f.toString().contains('employeeName: Must at least be 5 characters long')));
});
My unit test includes a predicate throwsA((f) => f.toString().contains('employeeName: Must at least be 5 characters long')) as given but dart fails this test with the exception:
package:moor/src/runtime/data_verification.dart 74:5 VerificationContext.throwIfInvalid
package:moor/src/runtime/query_builder/statements/insert.dart 197:51 InsertStatement._validateIntegrity
package:moor/src/runtime/query_builder/statements/insert.dart 96:5 InsertStatement.createContext
package:moor/src/runtime/query_builder/statements/insert.dart 64:17 InsertStatement.insert
package:moor_ex/src/db/employees.dart 79:76 EmployeesDB.createEmployee
test\employee_dept_test.dart 28:35 main.<fn>
InvalidDataException: Sorry, EmployeesCompanion(id: Value.absent(), employeeName: Value(Tony), employeeCode: Value(HR-121), address: Value(5th Floor, Park Avenue), contact: Value(1234567890), hiringDate: Value(2020-08-18 13:26:34.435349)) cannot be used for that because:
• employeeName: Must at least be 5 characters long.
So what's the correct way to write this test in Dart?
Try removing the await, or move it outside/before the expectLater.
The only difference between expect and expectLater is that the latter returns a future, but you ignore that future.
The expect/throwsA needs either a closure or a Future as the actual value, but you await that future so the await ... expression throws before you even call expectLater. Remove the await so you pass the Future to expectLater and the throwsA will then catch and match the error of that future.
So, I recommend changing it to:
await expectLater(employeesDB.createEmployee(employee), ...

Fable F# to Javascript: Parameterless functions being given a parameter when referenced

I am having difficulty referring to parameterless functions in Fable.
With this example:
let f1 () =
1
let someRefTof1 = f1
I'd expect the generated js to look something like this:
function f1() {
return 1;
}
var someRefTof1 = f1;
but what I actually get is:
function f1() {
return 1;
}
var someRefTof1 = exports.someRefTof1 = function someRefTof1(arg00_) {
return f1(arg00_);
};
I'm unclear on the purpose of arg00_ or how to avoid it being generated?
(As a bit of background, I am struggling to call a function in an external js library which expects a function to be passed as a parameter)
Edit:
Background
The above is what i believe to be a minimal, verifiable, reproduction of my question but, after comments, I thought it may be useful to provide a touch more context on why this is causing issues. What I am actually trying to do is use angularjs from Fable.
So my example looks more like this:
let app = AngularFable.NgFable.angular.``module``("app",[||])
type TestCtrl() =
member this.Val1() = "boom";
app?controller("test", TestCtrl)
which gets compiled to:
var app = exports.app = angular.module("app", []);
var TestCtrl = exports.TestCtrl = function () {
function TestCtrl() {
_classCallCheck(this, TestCtrl);
}
TestCtrl.prototype.Val1 = function Val1() {
return "boom";
};
return TestCtrl;
}();
_fableCore.Util.setInterfaces(TestCtrl.prototype, [], "App.TestCtrl");
app.controller("test", function (unitVar) {
return new TestCtrl();
});
with unitVar being the problematic parameter introduced in this example. When I use this in my html with something like:
<div ng-app="app">
<div ng-controller="test as vm">
{{vm.Val1()}}
</div>
</div>
I run into an unknown provider error whereas if I simply change the compiled javascript to remove the unitVar parameter from the last line like this:
app.controller("test", function () {
return new TestCtrl();
});
then my example works as expected.
I'd really like to know if there is a way to avoid having the Fable compiler generate this parameter. I'm 99% sure this reduces to the same problem as in my original question but I've included this additional context to better explain why this is an issue
Thank you very much for your question and detailed explanations. There're two things here that are a bit tricky and are caused by optimisations both of the F# compiler and Fable.
In the AST provided by the F# compiler, methods (functions that are members of a type or module) are compiled as usual methods as in C#. This is for optimization.
However, when you create an anonymous lambda or make a reference to a method, the F# compiler will keep F# semantics, that is, all functions have a single argument (as John Palmer says, unit is an argument too) and can be curried.
Ok, this info is just to make clear why the F# compiler/Fable represent methods and lambdas differently. Let's go with the issue of argumentless functions: the obvious solution would be of course to remove the F# compiler generated argument for functions accepting unit (as it's already done for methods). In fact, I also had problems with libraries like Mocha because of this.
I did try to remove the unit argument at the beginning but I got fails in some scenarios because of this. TBH, I don't remember now exactly which tests were failing but because of the expectation that there'll be always an argument, in some cases function composition or inlining was failing when the unit argument was removed.
Other attempts to modify the semantics of F# functions in the JS runtime have always failed because they don't cover all scenarios. However, we can be more lenient with delegates (System.Func<>) as it's usually safe to assume these ones should behave more like functions in languages like C# or F#. I can try to remove the unit argument just for delegates and see what happens :)
For more info about sending F# functions to JS code you can check the documentation.
UPDATE: Scratch all that, please try fable-compiler#0.6.12 and fable-core#0.6.8. This version eliminates unit arguments, the solution was actually simpler than I thought and (hopefully) shouldn't create issues with existing projects. (The explanation about methods and lambdas compiled differently still applies.)

DART: syntax of future then

I don´t understand the syntax of the then() clause.
1. myFuture(6).then( (erg) => print(erg) )
What´s (erg) => expr syntactically?
I thougt it could be a function, but
then( callHandler2(erg)
doesn´t work, Error:
"Multiple markers at this line
- The argument type 'void' cannot be assigned to the parameter type '(String) ->
dynamic'
- Undefined name 'erg'
- Expected to find ')'"
2. myFuture(5).then( (erg) { callHandler(erg);},
onError: (e) => print (e)
What´s `onError: (e) => expr"` syntactically?
3. Is there a difference between the onError: and the .catchError(e) variants?
1) The Fat Arrow is syntactic sugar for short anonymous functions. The two functions below are the same:
someFuture(arg).then((erg) => print(erg));
// is the same as
someFuture(arg).then((erg) { return print(erg); });
Basically the fat arrow basically automatically returns the evaluation of the next expression.
If your callHandler2 has the correct signature, you can just pass the function name. The signature being that it accept the number of parameters as the future will pass to the then clause, and returns null/void.
For instance the following will work:
void callHandler2(someArg) { ... }
// .. elsewhere in the code
someFuture(arg).then(callHandler);
2) See answer 1). The fat arrow is just syntactic sugar equivalent to:
myFuture(5).then( (erg){ callHandler(erg);}, onError: (e){ print(e); });
3) catchError allows you to chain the error handling after a series of futures. First its important to understand that then calls can be chained, so a then call which returns a Future can be chained to another then call. The catchError will catch errors both synchronous and asynchronous from all Futures in the chain. Passing an onError argument will only deal with an error in the Future its an argument for and for any synchronous code in your then block. Any asynchronous code in your then block will remain uncaught.
Recent tendency in most Dart code is to use catchError and omit the onError argument.
I will attempt to elaborate more on Matt's answer, hopefully to give more insights.
What then() requires is a function (callback), whose signature matches the future's type.
For example, given a Future<String> myFuture and doSomething being any function that accepts a String input, you can call myFuture.then(doSomething). Now, there are several ways to define a function that takes a String in Dart:
Function(String) doSomething1 = (str) => /* do something with str */ // only one command
Function(String) doSomething2 = (str) { /* do something with str */ } // several commands
Function(String) doSomething3 = myFunction;
myFunction(String) { // Dart will auto imply return type here
/* do something with str */ // several commands
}
Any of those 3 function definitions (the right hand side of =) could go inside then(). The first two definitions are called lambda functions, they are created at runtime and cannot be reused unless you manually copy the code. Lambda functions can potentially yield language-like expressions, i.e. (connection) => connection.connect(). The third approach allows the function to be reused. Lambda functions are common in many languages, you can read more about it here: https://medium.com/#chineketobenna/lambda-expressions-vs-anonymous-functions-in-javascript-3aa760c958ae.
The reason why you can't put callHandler2(erg) inside then() is because callHandler2(erg) uses an undefined variable erg. Using the lambda function, you will be able to tell then() that the erg in callHandler2(erg) is the output of the future, so it knows where to get erg value.

Global Setup and Teardown Functions

It's often the case that you need to do some sort of cleanup after every test. For instance, cleaning the database.
QUESTION:
Is there a way to attach global tearDown and setUp functions when using the unittest library?
It'd be even better to be able to define around advice:
unittest.around((test){
//doing setup
test();
//doing cleanup
});
Of course, the bug needs to be fixed first :-)
Then you would do this as Justin says' using non-anonymous functions that you can upcall explicitly. It seemed to me that this was the best way of providing maximum flexibility without encumbering the unit test library with a lot of convoluted setup/teardown logic.
If you want to do test suite setup/teardown, you could do that with an initial/final "test" in the group:
group('test suite', () {
test('Set up suite', () { ... });
test('Test 1', () { ... });
...
test('Test n', () { ... });
test('Tear down suite', () { ... });
});
It's not ideal, but it is a solution.
It's worth pointing out that internally, groups are not actually represented as a hierarchy. All we are really doing is keeping a stack with the current setUp/tearDown functions so we can associate each test case with the appropriate ones, and concatenating the group names and test name to make the final name for the test case. We are not building a tree data structure, so we don't have a good way of doing upcalls implicitly (we could create closures on the fly that upcall one level, and use those as the actual setUp/tearDown functions, but that's a bit fugly).
You can do it manually, as Gram alludes to in the bug:
main() {
topSetup() {
// ...
}
setUp(topSetup);
group('group', () {
setUp(() {
topSetup();
// ...
});
test('test', () {
// ...
});
});
}

Resources