Is there a way to fake DateTime.now() in a Flutter test? - testing

I can pass in a time to my widget, but my widget is supposed to be instantiating the time in itself, not receiving a DateTime from a parent widget.

The clock package: https://pub.dartlang.org/documentation/clock/latest/clock/clock-library.html lets you inject a fake clock into the test.

extension CustomizableDateTime on DateTime {
static DateTime customTime;
static DateTime get current {
if (customTime != null)
return customTime;
else
return DateTime.now();
}
}
Just use CustomizableDateTime.current in the production code. You can modify the returned value in tests like that: CustomizableDateTime.customTime = DateTime.parse("1969-07-20 20:18:04");. There is no need to use third party libraries.

As Mary said, a very neat way is to use the clock package, which maintained by the Dart team.
Normal usage:
import 'package:clock/clock.dart';
void main() {
// prints current date and time
print(clock.now());
}
Overriding the current time:
import 'package:clock/clock.dart';
void main() {
withClock(
Clock.fixed(DateTime(2000)),
() {
// always prints 2000-01-01 00:00:00.
print(clock.now());
},
);
}
I wrote about this in more detail on my blog.
Just note that for widget tests, you need to wrap pumpWidget, pump and expect in the withClock callback - only wrapping pumpWidget does not work.

Related

GoogleTest: is there a generic way to add a function call prior to each test case?

my scenario: I have an existing unit test framework with ~3000 individual test cases. They are made from TEST, TEST_F and TEST_P macros.
Internally the tested modules make use of a logger library and now my goal is to create individual log files for each test case. To do so I would like to call a function as a SetUp for each test case.
Is there a way to register such function at the framework and get it called automatically?
The obvious solution for me would look like: do the work in a test fixture constructor or SetUp() but then I'd have to touch every single test case.
I do like the idea of registering a global setup at the framework with AddGlobalTestEnvironment() but as I understand this is handled only once per executable.
By the way: I have acceptance tests implemented in robot test and guess what? I want to repeat the task there...
Thanks for any inspiration!
Christoph
You mentioned:
The obvious solution for me would look like: do the work in a test fixture constructor or SetUp() but then I'd have to touch every single test case.
If the reason that you think you would need to touch every single test case is to set the filename differently, you can use the combination of SetUp() function and the current_test_info provided by GTest to get the test name for each test, and then use that to create a separate file for each test.
Here is an example:
// Class for test fixture
class MyTestFixture : public ::testing::Test {
protected:
void SetUp() override {
test_name_ = std::string(
::testing::UnitTest::GetInstance()->current_test_info()->name());
std::cout << "test_name_: " << test_name_ << std::endl;
// CreateYourLogFileUsingTestName(test_name_);
}
std::string test_name_;
};
TEST_F(MyTestFixture, Test1) {
EXPECT_EQ(this->test_name_, std::string("Test1"));
}
TEST_F(MyTestFixture, Test2) {
EXPECT_EQ(this->test_name_, std::string("Test2"));
}
Live example here: https://godbolt.org/z/YjzEG3G77
The solution I found in the gtest docs:
class TraceHandler : public testing::EmptyTestEventListener
{
// Called before a test starts.
void OnTestStart( const testing::TestInfo& test_info ) override
{
// set the logfilename here
}
// Called after a test ends.
void OnTestEnd( const testing::TestInfo& test_info ) override
{
// close the log here
}
};
int main( int argc, char** argv )
{
testing::InitGoogleTest( &argc, argv );
testing::TestEventListeners& listeners =
testing::UnitTest::GetInstance()->listeners();
// Adds a listener to the end. googletest takes the ownership.
listeners.Append(new TraceHandler);
return RUN_ALL_TESTS();
}
This way it automatically applies to all tests linked to this main-function.
Maybe I have to mention: my logger is a collection of static functions that send udp-packets to a receiver that cares for actual logging. I can control the filename by one of that functions. That's the reason why I don't need to insert code in every single TEST, TEST_F or TEST_P.

How do I stub a function that does not belong to a class, during a widget test?

I am creating a flutter app that uses the native camera to take a photo, using the official flutter camera package (https://pub.dev/packages/camera). The app opens up a modal that loads a CameraPreview based on the the result of the availableCameras function from the package and a FloatingActionButton which takes a photo when pressed. While creating a widget test for this modal, I can not figure out how to stub the availableCameras function to return what I want during tests.
I tried using the Mockito testing package, but this only supports mocking classes. Since this function does not belong to a class, I cannot mock it.
The availableCameras function returns a list of cameras that the device has. I want to be able to control what comes back from this function, so that I may test how my widget reacts to different cameras. What is the proper way to have this function return what I want during a widget test?
Mockito can mock functions too. In dart, functions are classes with a call method.
You can, therefore, use Mockito as usual, with an abstract call method:
class MockFunction extends Mock {
int call(String param);
}
This example represents a int Function(String param).
Which means you can then do:
final int Function(String) myFn = MockFunction();
when(myFn('hello world')).thenReturn(42);
expect(myFn('hello world'), equals(42));
In this very specific situation, you can mock the method channel call handler.
const cameraMethodChannel = MethodChannel('plugins.flutter.io/camera');
setUpAll(() {
cameraMethodChannel.setMockMethodCallHandler(cameraCallHandler);
});
tearDownAll(() {
cameraMethodChannel.setMockMethodCallHandler(null);
});
Future<dynamic> cameraCallHandler(MethodCall methodCall) async {
if (methodCall.method == 'availableCameras') return yourListOfCameras;
}
Remi's answer here is correct. Here is a more complete recent example that returns a future. This article explains how you can build the widget test around your existing code. this uses mocktail.
import 'package:gistfile/main.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter/material.dart';
class LaunchMock extends Mock {
Future<bool> call(
Uri url, {
LaunchMode? mode,
WebViewConfiguration? webViewConfiguration,
String? webOnlyWindowName,
});
}
void main() {
testWidgets('Test Url Launch', (tester) async {
//These allow default values
registerFallbackValue(LaunchMode.platformDefault);
registerFallbackValue(const WebViewConfiguration());
//Create the mock
final mock = LaunchMock();
when(() => mock(
flutterDevUri,
mode: any(named: 'mode'),
webViewConfiguration: any(named: 'webViewConfiguration'),
webOnlyWindowName: any(named: 'webOnlyWindowName'),
)).thenAnswer((_) async => true);
final builder = compose()
//Replace the launch function with a mock
..addSingletonService<LaunchUrl>(mock);
await tester.pumpWidget(
builder.toContainer()<MyApp>(),
);
//Tap the icon
await tester.tap(
find.byIcon(Icons.favorite),
);
await tester.pumpAndSettle();
verify(() => mock(flutterDevUri)).called(1);
});
}

Can collections / iterables / streams be passed into #ParamterizedTests?

In Junit5 5.0.0 M4 I could do this:
#ParameterizedTest
#MethodSource("generateCollections")
void testCollections(Collection<Object> collection) {
assertOnCollection(collection);
}
private static Iterator<Collection<Object>> generateCollections() {
Random generator = new Random();
// We'll run as many tests as possible in 500 milliseconds.
final Instant endTime = Instant.now().plusNanos(500000000);
return new Iterator<Collection<Object>>() {
#Override public boolean hasNext() {
return Instant.now().isBefore(endTime);
}
#Override public Collection<Object> next() {
// Dummy code
return Arrays.asList("this", "that", Instant.now());
}
};
}
Or any number of other things that ended up with collections of one type or another being passed into my #ParameterizedTest. This no longer works: I now get the error
org.junit.jupiter.api.extension.ParameterResolutionException:
Error resolving parameter at index 0
I've been looking through the recent commits to SNAPSHOT and I there's a few changes in the area, but I can't see anything that definitely changes this.
Is this a deliberate change? I'd ask this on a JUnit5 developer channel but I can't find one. And it's not a bug per se: passing a collection is not a documented feature.
If this is a deliberate change, then this is a definite use-case for #TestFactory...
See https://github.com/junit-team/junit5/issues/872
The next snapshot build should fix the regression.

Authentication test running strange

I've just tried to write a simple test for Auth:
use Mockery as m;
...
public function testHomeWhenUserIsNotAuthenticatedThenRedirectToWelcome() {
$auth = m::mock('Illuminate\Auth\AuthManager');
$auth->shouldReceive('guest')->once()->andReturn(true);
$this->call('GET', '/');
$this->assertRedirectedToRoute('general.welcome');
}
public function testHomeWhenUserIsAuthenticatedThenRedirectToDashboard() {
$auth = m::mock('Illuminate\Auth\AuthManager');
$auth->shouldReceive('guest')->once()->andReturn(false);
$this->call('GET', '/');
$this->assertRedirectedToRoute('dashboard.overview');
}
This is the code:
public function getHome() {
if(Auth::guest()) {
return Redirect::route('general.welcome');
}
return Redirect::route('dashboard.overview');
}
When I run, I've got the following error:
EF.....
Time: 265 ms, Memory: 13.00Mb
There was 1 error:
1) PagesControllerTest::testHomeWhenUserIsNotAuthenticatedThenRedirectToWelcome
Mockery\Exception\InvalidCountException: Method guest() from Mockery_0_Illuminate_Auth_AuthManager should be called
exactly 1 times but called 0 times.
—
There was 1 failure:
1) PagesControllerTest::testHomeWhenUserIsAuthenticatedThenRedirectToDashboard
Failed asserting that two strings are equal.
--- Expected
+++ Actual
## ##
-'http://localhost/dashboard/overview'
+'http://localhost/welcome'
My questions are:
Two similar test cases but why the error output differs? First one the mock Auth::guest() is not called while the second one seems to be called.
On the second test case, why does it fail?
Is there any way to write better tests for my code above? Or even better code to test.
Above test cases, I use Mockery to mock the AuthManager, but if I use the facade Auth::shoudReceive()->once()->andReturn(), then it works eventually. Is there any different between Mockery and Auth::mock facade here?
Thanks.
You're actually mocking a new instance of the Illuminate\Auth\AuthManager and not accessing the Auth facade that is being utilized by your function getHome(). Ergo, your mock instance will never get called. (Standard disclaimer that none of the following code is tested.)
Try this:
public function testHomeWhenUserIsNotAuthenticatedThenRedirectToWelcome() {
Auth::shouldReceive('guest')->once()->andReturn(true);
$this->call('GET', '/');
$this->assertRedirectedToRoute('general.welcome');
}
public function testHomeWhenUserIsAuthenticatedThenRedirectToDashboard() {
Auth::shouldReceive('guest')->once()->andReturn(false);
$this->call('GET', '/');
$this->assertRedirectedToRoute('dashboard.overview');
}
If you check out Illuminate\Support\Facades\Facade, you'll see that it takes care of mocking for you. If you really wanted to do it the way that you were doing it (creating an instance of mock instance of Auth), you'd have to somehow inject it into the code under test. I believe that it could be done with something like this assuming that you extend from the TestCase class provided by laravel:
public function testHomeWhenUserIsNotAuthenticatedThenRedirectToWelcome() {
$this->app['auth'] = $auth = m::mock('Illuminate\Auth\AuthManager');
// above line will swap out the 'auth' facade with your facade.
$auth->shouldReceive('guest')->once()->andReturn(true);
$this->call('GET', '/');
$this->assertRedirectedToRoute('general.welcome');
}

In SourceMod, how do I check if a plugin exists?

In SourceMod, how do I check if a plugin exists? I tried the GetFeatureStatus method, but it doesn't work. Any ideas?
If a plugin has registered itself as a Library, you can check if it exists using the LibraryExists command on the name it registered. Traditionally, this name is in all lowercase, but some plugins/extensions use mixed-case, such as SteamTools (which uses "SteamTools").
Having said that, it's generally better to cache whether a library exists instead of constantly calling this command... but then a library can be unloaded or loaded on your without your knowledge. There are functions to catch that.
So, the best way is generally to do something like this (using the NativeVotes plugin as an example).
#undef REQUIRE_PLUGIN
#include <nativevotes>
//global variable
new bool:g_bNativeVotes = false;
public OnAllPluginsLoaded()
{
g_bNativeVotes = LibraryExists("nativevotes");
}
public OnLibraryAdded(const String:name[])
{
if (StrEqual(name, "nativevotes"))
{
g_bNativeVotes = true;
}
}
public OnLibraryRemoved(const String:name[])
{
if (StrEqual(name, "nativevotes"))
{
g_bNativeVotes = false;
}
}
If a plugin isn't registered as a library, you can use GetFeatureStatus to check for a particular native. The catch is in realizing that this function doesn't return a bool, but rather a FeatureStatus_ value.
For instance, here's how I'd check for a (in development) feature for the same plugin as mentioned above:
if (GetFeatureStatus(FeatureType_Native, "NativeVotes_IsVoteCommandRegistered") == FeatureStatus_Available)
{
// Do something with vote commands.
}