I'm migrating unit test to nnbd.
#! /usr/bin/env dcli
import 'dart:io';
import 'package:dcli/dcli.dart';
import 'package:args/args.dart';
/// dcli script generated by:
/// dcli create future.dart
///
/// See
/// https://pub.dev/packages/dcli#-installing-tab-
///
/// For details on installing dcli.
///
Future<void> main(List<String> args) async {
var testClient = TestClient();
final Future<TestResponse?> outageResponseFuture =
testClient.request('/outage').get();
// Introduce a delay to ensure that the /outage request gets there before /success
await Future<void>.delayed(const Duration(seconds: 1));
// ignore: unused_local_variable
final successResponse = await testClient.request('/success').get();
await outageResponseFuture.timeout(const Duration(milliseconds: 100),
onTimeout: () => Future.value(null));
}
class TestClient {
TestRequest request(String s) => TestRequest();
}
class TestRequest {
Future<TestResponse> get() => Future.value(TestResponse());
}
class TestResponse {}
name: stackoverflow
version: 0.0.1
description: A script generated by dcli.
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
args: ^2.0.0
dcli: ^0.51.0
path: ^1.0.0
dev_dependencies:
pedantic: ^1.0.0
include: package:pedantic/analysis_options.yaml
# For lint rules and documentation, see http://dart-lang.github.io/linter/lints.
# Uncomment to specify additional rules.
linter:
rules:
lines_longer_than_80_chars: false
camel_case_types: true
always_declare_return_types: true
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
The problem is with the call to timeout. When the code runs I get:
type '() => Future<TestResponse?>' is not a subtype of type '(() => FutureOr<TestResponse>)?' of 'onTimeout'
The problem is with the onTimeout returning null. I've tried declaring response with every possible combination of ? but it makes no difference.
Any clues?
Related
I'm looking to build the equivalent of the bash 'expect' command which is able to
capture output to stdout as well as inject data into stdin.
Dart provides the IOOverrides class that looks like it allows you to override stdout, stdin and stderr.
The ZoneSpecification that you pass to IOOverrides expects a instance of type StdIn to override stdin.
IOOverrides.runZoned(() => action,
stdin: () => Stdin._(mystream), // this won't work as there is no public ctor
);
As such I was hoping I could instantiate my own copy of StdIn and then inject data into it.
The problem is that StdIn only has a private constructor.
So it would appear that there is no way to actually override Stdin using a ZoneSpecification.
Am I missing something here?
The stdin getter actually has the code to allow it to be overriden:
/// The standard input stream of data read by this program.
Stdin get stdin {
return IOOverrides.current?.stdin ?? _stdin;
}
Are there other ways to achieve this?
Ultimately this is what I'm trying to achieve:
Interact(spawn: () {
final age = ask(
'How old are you',
defaultValue: '5',
customPrompt: (prompt, defaultValue, {hidden = false}) =>
'AAA$prompt:$defaultValue',
);
print('You are $age years old');
})
..expect('AAAHow old ar you:5')
..send('6')
..expect('You are 6 years old');
The Process class exposes a stdin member. See https://api.dart.dev/stable/2.10.5/dart-io/Process-class.html#standard-io-streams.
So this is not even close to a working solution, but based on #jamesdlin
comment the basic implementation looks like this.
Currently all of the puppet wrapper classes just forward calls to the real stdin/out/err. In reality these will need to intercept and process the calls rather than passing them through.
void test() {
Puppet(spawn: () {
final age = ask(
'How old are you',
defaultValue: '5',
customPrompt: (prompt, defaultValue, {hidden = false}) =>
'AAA$prompt:$defaultValue',
);
print('You are $age years old');
})
..expect('AAAHow old ar you:5')
..send('6')
..expect('You are 6 years old');
}
The Puppet class:
class Puppet<T> {
Puppet({required this.spawn});
T Function() spawn;
void _run() {
IOOverrides.runZoned(() => spawn,
stdin: PuppetStdin.new,
stdout: () => PuppetStdout(stdout),
stderr: () => PuppetStdout(stderr));
}
void expect(String expected) {}
void send(String s) {}
}
PuppetStdin
import 'dart:async';
import 'dart:convert';
import 'dart:io';
class PuppetStdin extends Stream<List<int>> implements Stdin {
PuppetStdin(
{this.echoMode = true,
this.echoNewlineMode = true,
this.lineMode = true});
StreamController<List<int>> controller = StreamController();
#override
bool echoMode;
#override
bool echoNewlineMode;
#override
bool lineMode;
#override
bool get hasTerminal => stdin.hasTerminal;
#override
StreamSubscription<List<int>> listen(void Function(List<int> event)? onData,
{Function? onError, void Function()? onDone, bool? cancelOnError}) {
throw UnimplementedError();
}
#override
int readByteSync() => stdin.readByteSync();
#override
String? readLineSync(
{Encoding encoding = systemEncoding, bool retainNewlines = false}) =>
stdin.readLineSync(encoding: encoding, retainNewlines: retainNewlines);
#override
bool get supportsAnsiEscapes => stdin.supportsAnsiEscapes;
}
PuppetStdout which will probably also serve the needs of overloading stderr
import 'dart:convert';
import 'dart:io';
class PuppetStdout implements Stdout {
PuppetStdout(this.stdout);
Stdout stdout;
IOSink? _nonBlocking;
/// Whether there is a terminal attached to stdout.
#override
bool get hasTerminal => stdout.hasTerminal;
/// The number of columns of the terminal.
///
/// If no terminal is attached to stdout, a [StdoutException] is thrown. See
/// [hasTerminal] for more info.
#override
int get terminalColumns => stdout.terminalColumns;
/// The number of lines of the terminal.
///
/// If no terminal is attached to stdout, a [StdoutException] is thrown. See
/// [hasTerminal] for more info.
#override
int get terminalLines => stdout.terminalLines;
/// Whether connected to a terminal that supports ANSI escape sequences.
///
/// Not all terminals are recognized, and not all recognized terminals can
/// report whether they support ANSI escape sequences, so this value is a
/// best-effort attempt at detecting the support.
///
/// The actual escape sequence support may differ between terminals,
/// with some terminals supporting more escape sequences than others,
/// and some terminals even differing in behavior for the same escape
/// sequence.
///
/// The ANSI color selection is generally supported.
///
/// Currently, a `TERM` environment variable containing the string `xterm`
/// will be taken as evidence that ANSI escape sequences are supported.
/// On Windows, only versions of Windows 10 after v.1511
/// ("TH2", OS build 10586) will be detected as supporting the output of
/// ANSI escape sequences, and only versions after v.1607 ("Anniversary
/// Update", OS build 14393) will be detected as supporting the input of
/// ANSI escape sequences.
#override
bool get supportsAnsiEscapes => stdout.supportsAnsiEscapes;
/// A non-blocking `IOSink` for the same output.
#override
IOSink get nonBlocking => _nonBlocking ??= stdout.nonBlocking;
#override
Encoding get encoding => stdout.encoding;
#override
set encoding(Encoding encoding) {
stdout.encoding = encoding;
}
#override
void add(List<int> data) {
stdout.add(data);
}
#override
void addError(Object error, [StackTrace? stackTrace]) {
stdout.addError(error, stackTrace);
}
#override
// ignore: strict_raw_type
Future addStream(Stream<List<int>> stream) => stdout.addStream(stream);
#override
// ignore: strict_raw_type
Future close() => stdout.close();
#override
// ignore: strict_raw_type
Future get done => stdout.done;
#override
// ignore: strict_raw_type
Future flush() => stdout.flush();
#override
void write(Object? object) {
stdout.write(object);
}
#override
// ignore: strict_raw_type
void writeAll(Iterable objects, [String sep = '']) {
stdout.writeAll(objects, sep);
}
#override
void writeCharCode(int charCode) {
stdout.writeCharCode(charCode);
}
#override
void writeln([Object? object = '']) {
stdout.writeln(object);
}
}
i'm using nestjs for project i have no issue using "#nestjs/config" before version 1.1.2 but when i created new one while copy paste from my old source code into my new one especially in main.ts it gives me error Argument of type 'string' is not assignable to parameter of type 'never'. when i passed string LISTENING_PORT into get function of configService but it worked in my old project what's the problem and how to solve this problem? thanks in advance.
here's my sample code
import { ConfigService } from '#nestjs/config';
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import * as helmet from 'helmet';
import { CustomExceptionFilter } from './exceptions/custom-exception.filter';
import { CustomValidationPipe } from './pipes/custom-validation.pipe';
import { SwaggerModule, DocumentBuilder } from '#nestjs/swagger';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors();
app.use(helmet());
app.useGlobalPipes(new CustomValidationPipe());
const configService = app.get(ConfigService);
app.useGlobalFilters(new CustomExceptionFilter(configService));
const listeningPort = configService.get('LISTENING_PORT');
const config = new DocumentBuilder()
.setTitle('API Documentation')
.setDescription('API description')
.setVersion('0.1')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(listeningPort);
}
bootstrap();
Fixed on #nestjs/config version 1.1.3
I have create a Dart (Dart v2) code generator to detect annotations of type Cooper.
The generator looks like this:
class CooperGenerator extends GeneratorForAnnotation<Cooper> {
#override
Future<String> generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) async {
final name = annotation?.peek("implementationClassName")?.stringValue ?? "CooperApi";
return "class $name {}";
}
}
I am writing unit tests to verify that my Generator works as expected. Looks like this:
import 'package:build/build.dart';
import 'package:test/test.dart';
import 'package:build_test/build_test.dart';
import 'package:source_gen/source_gen.dart';
import 'package:cooper_generator/cooper_generator.dart';
void main() {
group('CooperGenerator', () {
test('generates implementation for classes annotated with #Cooper',
() async {
final builder = PartBuilder([CooperGenerator()], '.g.dart');
await testBuilder(builder, _inputs, outputs: _output);
});
});
}
final _output = {
"jorge|lib/coop.g.dart": '''
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'coop.dart';
// **************************************************************************
// CooperGenerator
// **************************************************************************
class CooperImplementation {}
'''
};
final _inputs = <String, String>{
'cooper|lib/cooper.dart': _cooperSource,
'jorge|lib/coop.dart': _classToGenerate,
};
const String _classToGenerate = r'''
import 'package:cooper/cooper.dart';
#Cooper("CooperImplementation")
abstract class CooperContract {}
''';
const String _cooperSource = r'''
class Cooper {
final String implementationClassName;
const Cooper(this.implementationClassName);
}
''';
The code works perfectly fine, and it generates the code I am expecting. However, when running the unit tests, I am getting the following error, and I can't understand why:
ERROR: Expected: contains AssetId:<jorge|lib/coop.g.dart>
Actual: ?:[]
Builder failed to write asset jorge|lib/coop.g.dart
package:test expect
package:build_test/src/test_builder.dart 49:7 checkOutputs.<fn>
dart:collection __InternalLinkedHashMap&_HashVMBase&MapMixin&_LinkedHashMapMixin.forEach
package:build_test/src/test_builder.dart 41:13 checkOutputs
package:build_test/src/test_builder.dart 140:3 testBuilder
===== asynchronous gap ===========================
dart:async _AsyncAwaitCompleter.completeError
package:build_test/src/test_builder.dart testBuilder
===== asynchronous gap ===========================
dart:async _asyncThenWrapperHelper
package:build_test/src/test_builder.dart testBuilder
test/generator_test.dart 12:13 main.<fn>.<fn>
Does anyone know why this is giving me this error?
Trying to use Mockito to test my BLoC, the BLoC makes a server call using a repository class and the server call function is supposed to throw a custom exception if the user is not authenticated.
But when I am trying to stub the repository function to throw that custom exception, the test just fails with the following error:
sunapsis Authorization error (test error): test description
package:mockito/src/mock.dart 342:7 PostExpectation.thenThrow.<fn>
package:mockito/src/mock.dart 119:37 Mock.noSuchMethod
package:sunapsis/datasource/models/notifications_repository.dart 28:37 MockNotificationRepository.getNotificationList
package:sunapsis/blocs/notification_blocs/notification_bloc.dart 36:10 NotificationBloc.fetchNotifications
test/blocs/notification_blocs/notification_bloc_test.dart 53:48 main.<fn>.<fn>.<fn>
===== asynchronous gap ===========================
dart:async scheduleMicrotask
test/blocs/notification_blocs/notification_bloc_test.dart 53:7 main.<fn>.<fn>
And this is what my BLoC code looks like: fetchNotifications function calls the repository function and handles the response and errors. There are two catchError blocks, one handles AuthorizationException case and other handles any other Exception. Handling AuthorizationException differently because it will be used to set the Login state of the application.
notification_bloc.dart
import 'dart:async';
import 'package:logging/logging.dart';
import 'package:rxdart/rxdart.dart';
import 'package:sunapsis/datasource/dataobjects/notification.dart';
import 'package:sunapsis/datasource/models/notifications_repository.dart';
import 'package:sunapsis/utils/authorization_exception.dart';
class NotificationBloc {
final NotificationsRepository _notificationsRepository;
final Logger log = Logger('NotificationBloc');
final _listNotifications = PublishSubject<List<NotificationElement>>();
final _isEmptyList = PublishSubject<bool>();
final _isLoggedIn = PublishSubject<bool>();
Observable<List<NotificationElement>> get getNotificationList =>
_listNotifications.stream;
Observable<bool> get isLoggedIn => _isLoggedIn.stream;
Observable<bool> get isEmptyList => _isEmptyList.stream;
NotificationBloc({NotificationsRepository notificationsRepository})
: _notificationsRepository =
notificationsRepository ?? NotificationsRepository();
void fetchNotifications() {
_notificationsRepository
.getNotificationList()
.then((List<NotificationElement> list) {
if (list.length > 0) {
_listNotifications.add(list);
} else {
_isEmptyList.add(true);
}
})
.catchError((e) => _handleErrorCase,
test: (e) => e is AuthorizationException)
.catchError((e) {
log.shout("Error occurred while fetching notifications $e");
_listNotifications.sink.addError("$e");
});
}
void _handleErrorCase(e) {
log.shout("Session invalid: $e");
_isLoggedIn.sink.add(false);
_listNotifications.sink.addError("Error");
}
}
This is what my repository code looks like:
notifications_repository.dart
import 'dart:async';
import 'package:logging/logging.dart';
import 'package:sunapsis/datasource/dataobjects/notification.dart';
import 'package:sunapsis/datasource/db/sunapsis_db_provider.dart';
import 'package:sunapsis/datasource/network/api_response.dart';
import 'package:sunapsis/datasource/network/sunapsis_api_provider.dart';
import 'package:sunapsis/utils/authorization_exception.dart';
/// Repository class which makes available all notifications related API functions
/// for server calls and database calls
class NotificationsRepository {
final Logger log = Logger('NotificationsRepository');
final SunapsisApiProvider apiProvider;
final SunapsisDbProvider dbProvider;
/// Optional [SunapsisApiProvider] and [SunapsisDbProvider] instances expected for unit testing
/// If instances are not provided - default case - a new instance is created
NotificationsRepository({SunapsisApiProvider api, SunapsisDbProvider db})
: apiProvider = api ?? SunapsisApiProvider(),
dbProvider = db ?? SunapsisDbProvider();
/// Returns a [Future] of [List] of [NotificationElement]
/// Tries to first look for notifications on the db
/// if notifications are found that list is returned
/// else a server call is made to fetch notifications
Future<List<NotificationElement>> getNotificationList([int currentTime]) {
return dbProvider.fetchNotifications().then(
(List<NotificationElement> notifications) {
if (notifications.length == 0) {
return getNotificationsListFromServer(currentTime);
}
return notifications;
}, onError: (_) {
return getNotificationsListFromServer(currentTime);
});
}
}
The function getNotificationsListFromServer is supposed to throw the AuthorizationException, which is supposed to be propagated through getNotificationList
This is the test case that is failing with the error mentioned before:
test('getNotification observable gets error on AuthorizationException',
() async {
when(mockNotificationsRepository.getNotificationList())
.thenThrow(AuthorizationException("test error", "test description"));
scheduleMicrotask(() => notificationBloc.fetchNotifications());
await expectLater(
notificationBloc.getNotificationList, emitsError("Error"));
});
And this is what the custom exception looks like:
authorization_exception.dart
class AuthorizationException implements Exception {
final String error;
final String description;
AuthorizationException(this.error, this.description);
String toString() {
var header = 'sunapsis Authorization error ($error)';
if (description != null) {
header = '$header: $description';
}
return '$header';
}
}
PS: When I tested my repository class and the function throwing the custom exception those tests were passed.
test('throws AuthorizationException on invalidSession()', () async {
when(mockSunapsisDbProvider.fetchNotifications())
.thenAnswer((_) => Future.error("Error"));
when(mockSunapsisDbProvider.getCachedLoginSession(1536333713))
.thenAnswer((_) => Future.value(authorization));
when(mockSunapsisApiProvider.getNotifications(authHeader))
.thenAnswer((_) => Future.value(ApiResponse.invalidSession()));
expect(notificationsRepository.getNotificationList(1536333713),
throwsA(TypeMatcher<AuthorizationException>()));
});
Above test passed and works as expected.
I am a new college grad working my first full time role and I might be doing something wrong. I will really appreciate any feedback or help, everything helps. Thanks for looking into this question.
You're using thenThrow to throw an exception, but because the mocked method returns a Future you should use thenAnswer.
The test would be like that:
test('getNotification observable gets error on AuthorizationException', () async {
// Using thenAnswer to throw an exception:
when(mockNotificationsRepository.getNotificationList())
.thenAnswer((_) async => throw AuthorizationException("test error", "test description"));
scheduleMicrotask(() => notificationBloc.fetchNotifications());
await expectLater(notificationBloc.getNotificationList, emitsError("Error"));
});
I think you are using the wrong TypeMatcher class. You need to use the one from the testing framework and not the one from the Flutter framework.
import 'package:flutter_test/flutter_test.dart';
import 'package:matcher/matcher.dart';
class AuthorizationException implements Exception {
const AuthorizationException();
}
Future<List<String>> getNotificationList(int id) async {
throw AuthorizationException();
}
void main() {
test('getNotification observable gets error on AuthorizationException',
() async {
expect(getNotificationList(1536333713),
throwsA(const TypeMatcher<AuthorizationException>()));
});
}
When i try to use future.sync from class future like
import 'package:async/async.dart';
import 'dart:async';
void main() {
var fur3 = new Future<int>(() => 45);
int z = Future.sync(fur3);
print(z);
}
i've got the error message
Breaking on exception: object of type NoSuchMethodError
Do i use future.sync in the wrong way?
My second question is
import 'package:async/async.dart';
void main() {
var fur1 = new Future<int>(() => 45);
fur1.then((value) {
return value;
}).catchError((err) => print('catchError1: ${err}'));
}
why when i try to import async library from package, i've got compiler message
Breaking on exception: object of type TypeError
Undefined class 'Future'
what am i do here wrong?
You don't pass a future into Future.sync() but a closure to be executed immediately.
Future z = Future.sync(() => print('bla'));
async is an internal package. You import it using
import 'dart:async';
Internal packages don't need to be added to pubspec.yaml dependencies because they are always available. The different import statement is related to that.