I have an asynchronous list (toListAsync) and I need to perform a linq select statement on it but it doesn't work because Task does not contain a definition for select.
What can I do?
public async Task<List<FilesTable>> List()
{
using (var ctx = Configuration.OpenContext(false))
{
return await ctx.FilesTables.Include(e => e.Subsection).ToListAsync();
}
}
// The above is in the first file(FilesTable.cs),
// the below in a different file(file_apis.cs)
public async Task<List<FilesModel>> GetFiles()
{
return await new FilesTable().List().Select(e => (FilesModel)e).ToList();
}
Add using System.Linq; namespace.
Then you can do this:
(await yourMethod.ToListAsync()).Select(a => new { });
You should add parenthesis before await keyword and after ToListAsync() method
From performance PoV it is better to use filters first, then at the end use ToListAsync, so you can use:
MyList.Where(x => ...).Select(x => ...).ToListAsync();
Otherwise you will be loading unnecessary amount of data to the memory, and this will reduce the performance depending on the amount of data. But if you do filter before ToList only the required data will be loaded.
[Update]
With reference to your code, you can a new method to the FilesTable.cs that will do filter and return the desired list:
public async Task<List<FilesModel>> ListFiles()
{
using(var cts = Configuration.OpenContext(false))
{
return await ctx.FilesTables.Include(e => e.Subsection).Select(e => (FilesModel)e).ToListAsync();
}
}
You can use or brekets for waiting the iteration end for doing the select, or you can use IAsyncEnumerable
public async IAsyncEnumerable<FilesTable> List()
{
using (var ctx = Configuration.OpenContext(false))
{
return ctx.FilesTables.Include(e => e.Subsection).AsAsyncEnumerable();
}
}
public async IAsyncEnumerable<FilesModel> GetFiles()
{
await foreach(var value in new FilesTable().List())
{
yield return value;
}
}
// or using that nuget package System.Linq.Async NuGet package
public async Task<List<FilesModel>> GetFiles()
{
return await new FilesTable().List().ToListAsync();
}
Related
This is my first app with Dart/Flutter/Riverpod, so any advice or comment about the code is welcome.
I'm using Hive as embedded db so the initial value for the provider's state is loaded asynchronously and using an AsyncValue of riverpod to wrapped it.
The following code works but I've got some doubts about the testing approach, so I would like to confirm if I'm using the Riverpod lib as It supposed to be used.
This is my provider with its deps (Preferences is a HiveObject to store app general config data):
final hiveProvider = FutureProvider<HiveInterface>((ref) async {
return await App.setUp();
});
final prefBoxProvider = FutureProvider<Box<Preferences>>((ref) async {
final HiveInterface hive = await ref.read(hiveProvider.future);
return hive.openBox<Preferences>("preferences");
});
class PreferencesNotifier extends StateNotifier<AsyncValue<Preferences>> {
late Box<Preferences> prefBox;
PreferencesNotifier(Future<Box<Preferences>> prefBoxFuture): super(const AsyncValue.loading()) {
prefBoxFuture.then((value) {
prefBox = value;
_loadCurrentPreferences();
});
}
void _loadCurrentPreferences() {
Preferences pref = prefBox.get(0) ?? Preferences();
state = AsyncValue.data(pref);
}
Future<void> save(Preferences prefs) async {
await prefBox.put(0, prefs);
state = AsyncValue.data(prefs);
}
Preferences? get preferences {
return state.when(data: (value) => value,
error: (_, __) => null,
loading: () => null);
}
}
final preferencesProvider = StateNotifierProvider<PreferencesNotifier, AsyncValue<Preferences>>((ref) {
return PreferencesNotifier(ref.read(prefBoxProvider.future));
});
And the following is the test case, I'm mocking the Hive box provider (prefBoxProvider):
class Listener extends Mock {
void call(dynamic previous, dynamic value);
}
Future<Box<Preferences>> prefBoxTesting() async {
final hive = await App.setUp();
Box<Preferences> box = await hive.openBox<Preferences>("testing_preferences");
await box.clear();
return box;
}
void main() {
test('Preferences value changes', () async {
final container = ProviderContainer(overrides: [
prefBoxProvider.overrideWithValue(AsyncValue.data(await prefBoxTesting()))
],);
addTearDown(() {
container.dispose();
Hive.deleteBoxFromDisk("testing_preferences");
});
final listener = Listener();
container.listen<AsyncValue<Preferences>>(
preferencesProvider,
listener,
fireImmediately: true,
);
verify(listener(null, const TypeMatcher<AsyncLoading>())).called(1);
verifyNoMoreInteractions(listener);
// Next line waits until we have a value for preferences attribute
await container.read(preferencesProvider.notifier).stream.first;
verify(listener(const TypeMatcher<AsyncLoading>(), const TypeMatcher<AsyncData>())).called(1);
Preferences preferences = Preferences.from(container.read(preferencesProvider.notifier).preferences!);
preferences.currentListName = 'Lista1';
await container.read(preferencesProvider.notifier).save(preferences);
verify(listener(const TypeMatcher<AsyncData>(), const TypeMatcher<AsyncData>())).called(1);
verifyNoMoreInteractions(listener);
final name = container.read(preferencesProvider.notifier).preferences!.currentListName;
expect(name, equals('Lista1'));
});
}
I've used as reference the official docs about testing Riverpod and the GitHub issue related with AsyncValues
Well, I found some problems to verify that the listener is called with the proper values, I used the TypeMatcher just to verify that the state instance has got the proper type and I check ("manually") the value of the wrapped object's attribute if It's the expected one. Is there a better way to achieve this ?
Finally, I didn't find too many examples with StateNotifier and AsyncValue as state type, Is there a better approach to implement providers that are initialized with deferred data ?
I didn't like too much my original approach so I created my own Matcher to compare wrapped values in AsyncValue instances:
class IsWrappedValueEquals extends Matcher {
final dynamic value;
IsWrappedValueEquals(this.value);
#override
bool matches(covariant AsyncValue actual, Map<dynamic, dynamic> matchState) =>
equals(actual.value).matches(value, matchState);
#override
Description describe(Description description) => description.add('Is wrapped value equals');
}
In the test, the final part is a bit different:
Preferences preferences = Preferences.from(container.read(preferencesProvider.notifier).preferences!);
preferences.currentListName = 'Lista1';
await container.read(preferencesProvider.notifier).save(preferences);
// the following line is the new one
verify(listener(IsWrappedValueEquals(Preferences()), IsWrappedValueEquals(preferences))).called(1);
verifyNoMoreInteractions(listener);
}
I prefer my custom Matcher to the original code, but I feel that there are too many custom code to test something, apparently, common.
If anyone can tell me a better solution for this case, It'd be great.
I am new to FakeItEasy and I am having a problem asserting if an async method has been called. It fails assertion because it hasn't been called. I have done my best to ensure the assertion matches the configured call but still no dice.
[HttpPost]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status201Created)]
public async Task<IActionResult> Post(CreateOwnerRequest requestModel)
{
var command = new CreateOwnerCommand { RequestModel = requestModel };
var ownerResponse = await _mediator.Send(command, default);
//TODO: return URI for the new resources
return Created("", ownerResponse);
}
[Theory]
[ClassData(typeof(ValidCreateOwnerTestData))]
public async void ShouldCallCreateOwnerHandler(CreateOwnerRequest validCreateOwnerModel)
{
// Arrange
var fakeMediator = A.Fake<IMediator>();
A.CallTo(() => fakeMediator.Send(new CreateOwnerCommand { RequestModel = validCreateOwnerModel },
default)).Returns(A.Dummy<Task<OwnerResponse>>());
var ownerController = new OwnerController(fakeMediator);
// Act
_ = await ownerController.Post(validCreateOwnerModel);
// Assert
A.CallTo(() => fakeMediator.Send(new CreateOwnerCommand { RequestModel = validCreateOwnerModel },
default)).MustHaveHappened();
}
public class ValidCreateOwnerTestData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { new CreateOwnerRequest { FirstName = "Foo", LastName = "Bar" } };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
In your assertion, you create a new instance of CreateOwnerRequest. Obviously, this isn't the same instance that is actually used in the controller action, so it isn't considered equal, and the assertion fail.
You could override Equals on CreateOwnerRequest so that they are considered equal, but it's probably not a good idea, since you would do it only in order to satisfy the assertion.
A better approach is to use argument constraints to specify a condition that the argument must match:
// Assert
A.CallTo(() => fakeMediator.Send(
A<CreateOwnerCommand>.That.Matches(command => /* some condition here */))
A<CancellationToken>.Ignored))
.MustHaveHappened();
(note that I also changed default to A<CancellationToken>.Ignored, because you probably don't want the assertion to fail if the controller action starts using a real cancellation token...)
Sorry if this seems a dumb question. I'm learning clean architecture as dictated by Rob Martin, and I've having a tiny bit of trouble writing one of my tests.
I wrote a couple functions in a Hive repo. Here's the code
import 'package:hive/hive.dart';
import 'package:movie_browser/features/SearchMovie/domain/entities/movie_detailed_entity.dart';
abstract class HiveMovieSearchRepoAbstract {
Future<void> cacheMovieDetails(MovieDetailed movie);
Future<MovieDetailed> getCachedMovieDetails(String id);
}
// const vars to prevent misspellings
const String MOVIEDETAILSBOX = "MovieDetailedBox";
const String SEARCHBOX = "SearchBox";
class HiveMovieSearchRepo implements HiveMovieSearchRepoAbstract {
Box movieDetailsBox = Hive.box(MOVIEDETAILSBOX) ?? null;
// TODO implement searchbox
// final searchBox = Hive.box(SEARCHBOX);
Future<void> cacheMovieDetails(MovieDetailed movie) async {
/// expects a MovieDetailed to cache. Will cache that movie
movieDetailsBox ?? await _openBox(movieDetailsBox, MOVIEDETAILSBOX);
movieDetailsBox.put('${movie.id}', movie);
}
Future<MovieDetailed> getCachedMovieDetails(String id) async {
/// expects a string id as input
/// returns the MovieDetailed if cached previously
/// returns null otherwise
movieDetailsBox ?? await _openBox(movieDetailsBox, MOVIEDETAILSBOX);
return await movieDetailsBox.get('$id');
}
_openBox(Box box, String type) async {
await Hive.openBox(type);
return Hive.box(type);
}
}
I can't think of how to test this? I want two cases, one where the box is already opened, and one case where it isn't.
Specifically, it's these lines I want to test
movieDetailsBox ?? await _openBox(movieDetailsBox, MOVIEDETAILSBOX);
_openBox(Box box, String type) async {
await Hive.openBox(type);
return Hive.box(type);
}
I thought about mocking the Box object then doing something like....
when(mockHiveMovieSearchRepo.getCachedMovieDetails(some_id)).thenAnswer((_) async => object)
but wouldn't that bypass the code I want tested and always show as positive?
Thanks so much for the help
i don't know if i fully understand your question but you can try something like this
abstract class HiveMovieSearchRepoAbstract {
Future<void> cacheMovieDetails(MovieDetailed movie);
Future<MovieDetailed> getCachedMovieDetails(String id);
}
// const vars to prevent misspellings
const String MOVIEDETAILSBOX = "MovieDetailedBox";
const String SEARCHBOX = "SearchBox";
class HiveMovieSearchRepo implements HiveMovieSearchRepoAbstract {
final HiveInterface hive;
HiveMovieSearchRepo({#required this.hive});
#override
Future<void> cacheMovieDetails(MovieDetailed cacheMovieDetails) async {
/// expects a MovieDetailed to cache. Will cache that movie
try {
final moviedetailbox = await _openBox(MOVIEDETAILSBOX);
moviedetailbox.put('${movie.id}', movie);
} catch (e) {
throw CacheException();
}
}
Future<MovieDetailed> getCachedMovieDetails(String id) async {
/// expects a string id as input
/// returns the MovieDetailed if cached previously
/// returns null otherwise
try {
final moviedetailbox = await _openBox(MOVIEDETAILSBOX);
if (moviedetailbox.containsKey(boxkeyname)) {
return await movieDetailsBox.get('$id');
}
return null;
} catch (e) {
return CacheException();
}
}
Future<Box> _openBox(String type) async {
try {
final box = await hive.openBox(type);
return box;
} catch (e) {
throw CacheException();
}
}
}
And to test it you can do something like this
class MockHiveInterface extends Mock implements HiveInterface {}
class MockHiveBox extends Mock implements Box {}
void main() {
MockHiveInterface mockHiveInterface;
MockHiveBox mockHiveBox;
HiveMovieSearchRepo hiveMovieSearchRepo;
setUp(() {
mockHiveInterface = MockHiveInterface();
mockHiveBox = MockHiveBox();
hiveMovieSearchRepo = HiveMovieSearchRepo(hive: mockHiveInterface);
});
group('cacheMoviedetails', () {
test(
'should cache the movie details',
() async{
//arrange
when(mockHiveInterface.openBox(any)).thenAnswer((_) async => mockHiveBox);
//act
await hiveMovieSearchRepo.cacheMovieDetails(tcacheMovieDetails);
//assert
verify(mockHiveBox.put('${movie.id}', tmovie));
verify(mockHiveInterface.openBox("MovieDetailedBox"));
});
});
group('getLocalCitiesAndCountriesAtPage', () {
test('should when', () async {
//arrange
when(mockHiveInterface.openBox(any))
.thenAnswer((realInvocation) async => mockHiveBox);
when(mockHiveBox.get('$id'))
.thenAnswer((realInvocation) async => tmoviedetails);
//act
final result =
await hiveMovieSearchRepo.getCachedMovieDetails(tId);
//assert
verify(mockHiveInterface.openBox(any));
verify(mockHiveBox.get('page${tpage.toString()}'));
expect(result, tmoviedetails);
});
});
}
You should add some tests also for the CacheExeption().
Hope this help you.
So, I wrote this post 9 months. Stackoverflow just sent me a notification saying it's a popular question, so I'll answer it for anyone else wondering the same thing
Easy way to make this testable is change Box to an arg passed into the class, like so
abstract class ClassName {
final Box movieDetailsBox;
final Box searchBox;
ClassName({
this.moveDetailsBox,
this.searchBox,
});
}
this makes the boxes mockable and testable
You should mock the hive interface and box;
I have a routine which gets a list of filenames from the device, then reads the file(s) to build a list. However, the calling routine always returns with zero items. I print the filenames, so I know they exist, however, it appears that the async is returning before I read the files. I used similar code when making an HTTP call. But, something here is causing the routine to return the list even though it hasn't completed. Perhaps, it is possible that I am calling it at the wrong time? I am calling retrieveItems here:
#override
void initState() {
super.initState();
retrieveItems();
}
Eventually I will have a refresh button, but for now I'd simply like the list to populate with the data from the files...
--------------------
Callee
Future<List<String>> readHeaderData() async {
List<String> l = new List();
List<String> files = await readHeaders(); // Gets filenames
files.forEach((filename) async {
final file = await File(filename);
String contents = await file.readAsString();
User usr = User.fromJson(json.decode(contents));
String name = usr.NameLast + ", " + usr.NameFirst;
print(name);
l.add(name);
}
return l;
Caller
void retrieveItems() async {
LocalStorage storage = new LocalStorage();
await storage.readHeaderData().then((item) {
try {
if ((item != null ) &&(item.length >= 1)) {
setState(() {
users.clear();
_users.addAll(item);
});
} else {
setState(() {
_users.clear();
final snackbar = new SnackBar(
content: new Text('No users found.'),
);
scaffoldKey.currentState.showSnackBar(snackbar);
});
}
} on FileNotFoundException catch (e) {
print(e.toString()); //For debug only
setState(() {
_users.clear();
});
});
}
});
This code
Future<List<String>> readHeaderData() async {
List<String> l = new List();
List<String> files = await readHeaders(); // Gets filenames
files.forEach((filename) async {
final file = await File(filename);
String contents = await file.readAsString();
User user = User.fromJson(json.decode(contents));
String name = user.NameLast + ", " + user.NameFirst;
print(name);
l.add(name);
}
return l;
}
returns the list l and then processes the asyc forEach(...) callbacks
If you change it to
Future<List<String>> readHeaderData() async {
List<String> l = new List();
List<String> files = await readHeaders(); // Gets filenames
for(var filename in files) { /// <<<<==== changed line
final file = await File(filename);
String contents = await file.readAsString();
User user = User.fromJson(json.decode(contents));
String name = user.NameLast + ", " + user.NameFirst;
print(name);
l.add(name);
}
return l;
}
the function will not return before all filenames are processed.
files.forEach((filename) async {
means that you can use await inside the callback, but forEach doesn't care about what (filename) async {...} returns.
Also possible
await Future.forEach(yourList, (T elem) async { ...async staff });
To expand on Günter's comment regarding using list.map(f), here's an example of converting a forEach call so that it works correctly.
Broken example
Incorrectly assumes forEach will wait on futures:
Future<void> brokenExample(List<String> someInput) async {
List<String> results;
someInput.forEach((input) async {
String result = await doSomethingAsync(input);
results.add(result);
});
return results;
}
Corrected example
Waits on the async functions to complete, using Future.wait and .map():
Future<void> correctedExample(List<String> someInput) async {
List<String> results;
await Future.wait(someInput.map((input) async {
String result = await doSomethingAsync(input);
results.add(result);
}));
return results;
}
I encountered the similar issue. The problem is that dart will NOT wait for "forEach" contrary to public believe. There are two solutions:
1) Convert forEach to for loop as indicated by others. Another is use Future:
2) await Future.forEach(list, (item) async {
// your code
final result = await getMyResult();
});
Another option
Future.wait(someList.map((item) => something_returns_future(item)));
I have a middleware method that requires context.Authentication.User.Identity.Name to be resolved for proper execution. However, when writing a unit test these properties are obviously null as no sign-in has occurred. I am not using Oauth or anything authentication related in this middleware (beyond the obvious name property), as it should be handled elsewhere in another middleware (to promote re-use/flexibility of the component I am developing). Is there a way to mock/fake this value so I can run my test? I have tried everything I can think of to fake a sign-on and I am just stuck at this point. To be clear the middleware needs the value not a webapi call or the like.
//Arrange
var resolver = A.Fake<IDependencyResolver>();
A.CallTo(() => resolver.GetService(typeof(ISomeService))).Returns(new TestService());
using (var server = TestServer.Create(app =>
{
app.UseMyMiddleware(new MyMiddlewareOptions()
{
DependencyResolver = resolver
});
app.Run(async ctx =>
{
await ctx.Response.WriteAsync(ctx.Request.Path.Value);
});
}))
{
//Act
var response = await server.CreateRequest("/").GetAsync();
//Assert
A.CallTo(() => resolver.GetService(typeof(ISomeService)))
.MustHaveHappened(Repeated.Exactly.Once);
Assert.AreEqual(response.StatusCode, HttpStatusCode.OK);
//Etc.
}
So here is one way I suppose not thrilled with it but it does the job. I will wait to accept as I imagine there should be a better way.
public class TestFakeLoginMiddleware : OwinMiddleware
{
public TestFakeLoginMiddleware(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
var identity = A.Fake<IIdentity>();
A.CallTo(() => identity.Name).Returns("TEST#domain.local");
var user = new ClaimsPrincipal(identity);
context.Request.Context.Authentication.User = user;
await Next.Invoke(context);
}
}
A bit late, I know, but could you not just create a new ClaimsIdentity?
public override async Task Invoke(IOwinContext context)
{
var identity= new ClaimsIdentity(new List<Claim> {
new Claim(ClaimTypes.Name, "TEST#domain.local")
});
var user = new ClaimsPrincipal(identity);
context.Request.Context.Authentication.User = user;
await Next.Invoke(context);
}