Flutter Mockito verify that callback passed to widget is called - testing

I have a widget that takes a callback which is called when a button is pressed. I am trying to test that the callback is correctly invoked by the button.
I've tried mocking a Function class:
class MockCallback extends Mock implements Function {
call() {}
}
Then passing an instance of the mock class to my widget and simulating a tap:
final mocked = MockCallback();
await tester.pumpWidget(
MyWidget(myCallback: mocked),
);
final clearButtonFinder = find.byType(IconButton);
await tester.tap(clearButtonFinder);
verify(mocked()).called(1);
This results in an error on the verify call saying Used on a non-mockito object. If I put a print statement inside the mocked call, I can see that the tap is indeed calling it.
How can I verify that the callback passed to my widget is getting called once when the button is tapped?

This is how I solved this problem.
class MockCallback {
int _callCounter = 0;
void call() {
_callCounter += 1;
}
bool called(int expected) => _callCounter == expected;
void reset() {
_callCounter = 0;
}
}
No mockito needed.

Probably it is not the best solution - use a stream:
final callbackCalled = BehaviorSubject<void>.seeded(null);
await tester.pumpWidget(
MyWidget(myCallback: () { callbackCalled.add(null); }),
);
//... actions to trigger the callback
await expectLater(callbackCalled, emitsInOrder(<void>[null, null]));
You can use something meaningful instead of 'void' and 'null'.

Related

FakeItEasy ControllerTest HttpGet Calls

I want to start using FakeItEasy for testing queries.
The tests I want to write should check if entities are returned on HttpGet calls (get all and get by Id)
The Controller:
public class ToDoController : ControllerBase
{
private readonly IMediator _mediator;
public ToDoController(IMediator mediator) =>
_mediator = mediator;
[HttpGet]
[Produces("application/json")]
[ProducesResponseType(typeof(IEnumerable<ToDoItem>), (int)HttpStatusCode.OK)]
public async Task<ActionResult<IEnumerable<ToDoItem>>> Get()
{
var result = await _mediator.Send(new ToDoItemsQuery(new
AllToDoItems())).ConfigureAwait(false);
if (result != null && result.Any())
{
return result.ToList();
}
throw new InvalidOperationException("TODO: error handling");
}
[HttpGet]
[Route("{id}")]
[Produces("application/json")]
[ProducesResponseType(typeof(ToDoItem), (int)HttpStatusCode.OK)]
public async Task<ActionResult<ToDoItem>> GetById(int itemId)
{
var result = await _mediator
.Send(new ToDoItemsQuery(new ToDoItemById(itemId)))
.ConfigureAwait(false);
if (result != null && result.Any())
{
return result.FirstOrDefault();
}
throw new InvalidOperationException("TODO: error handling");
}
}
}
The TestClass:
public class ToDoItemControllerTests : ControllerTestBase
{
private IMediator _mediator;
private ToDoController _sut;
public ToDoItemControllerTests()
{
_mediator = A.Fake<IMediator>();
_sut = new ToDoController(_mediator);
}
[TestMethod]
public async Task GetAllItemsAsync_SuccessTest()
{
A.CallTo(() => _mediator.Send(A<AllToDoItems>._,
A<CancellationToken>._)).Returns(A.CollectionOfFake<ToDoItem>(10));
var result = await _sut.Get();
Assert.IsNotNull(result);
A.CallTo(() => _mediator).MustHaveHappened();
}
[TestMethod]
public async Task GetItemByIdAsync_SuccessTest()
{
// Arrange
int itemId = 2;
var commandResult =
new List<ToDoItem>
{
new ToDoItem
{
Id = itemId
};
}
A.CallTo(() => MediatR.Send(A<ToDoItemById>._, A<CancellationToken>._)).Returns(commandResult);
// Act
var result = await _sut.GetById(itemId);
// Assert
Assert.IsNotNull(result);
A.CallTo(() => MediatR.Send(A<ToDoItemById>._, A<CancellationToken>._)).MustHaveHappened();
}
}
So in the first test I set up A.CallTo the interface IMediatR to return 10 ToDoItems.
During debug I see the _sut.Get() enter the controller, entering the correct method/api call.
The _mediator.Send() in the controller returns a Fake IEnumerable (not the 10 items i set up in the first Call.To in the testmethod, but an enumeration that yields no results).
Because of the result.Any() being false, the controller throws an InvalidOperationException
And I cannot even Assert the result.IsNotNull()
The second test I want to Test If 1 item is returned upon calling the API.
I set up (a) an itemId of type int for parameter,
(b) A mocked(?) List with 1 Item from the setup with the itemId and
(c) a call to the mediatR should return the mocked Listfrom (b)
I make the call from the test, in debug I see the call await _mediator.Sent() returning
A Fake Ienumerable of ToDoItem, result is not null, but because result.Any() is false,
the item doesn't get returned, and I get another InvalidOperationException
I feel like I'm missing something in the setup of the tests..
A Fake database Interface?
I don't want to chance my controller and make the if less restrictive, just so my test would pass
EDIT:
Even if I change the if condition to removing the Any condition
and I see the test entering the controller, returning "First Or Default" of the result,
The test fails on A Call To Must Have Happened.
Expected to find it once or more but no calls were made to the fake object.
This I really don't get, i actually see him making the call?!
I've browsed GitHub to find examples but the closest I found was Entities with methods, with those methods being defined in an interface. This is not the case here
Seeing as the official documentation doesn't make me any wiser I turn to SO <3
Thanks in advance!
In the first test, you configure the call to Send with an argument of type AllToDoItems. But in the controller, you actually call Send with a TodoItemsQuery. So the call doesn't match, and the default (unconfigured) behavior, which is to return a fake IEnumerable, applies. You need to configure the call like this:
A.CallTo(() => _mediator.Send(A<TodoItemsQuery>._,
A<CancellationToken>._)).Returns(A.CollectionOfFake<ToDoItem>(10));
In the second test, the problem is the same, with ToDoItemById instead of AllToDoItems

can not read get property of undefined angular 2 error

hi I am trying to get city name from google api but getting that error below is my code
appcomponent class
import {Component, OnInit} from 'angular2/core';
import {marketComponent} from './market.component';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {introComponent} from './intro.component';
import {geoService} from './service.geo';
import {JSONP_PROVIDERS} from 'angular2/http';
declare var google: any;
#Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [ROUTER_DIRECTIVES],
providers: [JSONP_PROVIDERS, geoService]
})
#RouteConfig([
{ path: '/intro', name: 'Intro', component: introComponent, useAsDefault: true },
{ path: '/market', name: 'Market', component: marketComponent },
])
export class AppComponent {
constructor(private _http: geoService) { }
public maps;
public cat_error: Boolean = false;
public xml_Latitude :string;
public xml_Lang: string;
ngOnInit() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(this.showPosition);
} else {
alert("Geolocation is not supported by this browser.");
}
var input: any = document.getElementById('google_places_ac');
var autocomplete = new google.maps.places.Autocomplete(input, {});
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var place = autocomplete.getPlace();
console.log(place)
});
}
showPosition(position) {
this.xml_Latitude = position.coords.latitude;
this.xml_Lang = position.coords.longitude;
this._http.getPlaces(this.xml_Latitude, this.xml_Lang).subscribe(
data => { this.maps = data },
err => { this.cat_error = true }
);
var result = this.maps.results;
var city = result[0].address_components[4].long_name + "," + result[0].address_components[6].long_name;
alert(city);
}
}
and geoservice file
import {Injectable} from 'angular2/core';
import { Response, Jsonp} from 'angular2/http';
import 'rxjs/add/operator/map';
#Injectable()
export class geoService {
constructor(private http: Jsonp) { }
public xml_Latitude: string;
public xml_Lang: string;
public getPlaces(xml_Latitude, xml_Lang) {
return this.http.get(`http://maps.googleapis.com/maps/api/geocode/json?latlng=
'${this.xml_Latitude}','${this.xml_Lang}'&sensor=true`)
.map((res: Response) => res.json())
.catch(this.handleError);
}
private handleError(error: Response) {
console.error(error);
return error.json().error || 'Server error';
}
}
error also says getplaces is not a function, I think I am missing something but don't know what....
In addition to the callback ordering problem identified by Thierry, you have a lost this context on this line:
navigator.geolocation.getCurrentPosition(this.showPosition);
The Problem
You have the classic JavaScript problem known as the incorrect this context.
The this keyword in JavaScript behaves differently than in does in other languages like C# and Java.
How this works
The this keyword, in a function, is determined as follows:
* If the function was created through a call to .bind, the this value is the argument provided to bind
* If the function was invoked through a method call, e.g. expr.func(args), then this is expr
* Otherwise
* If the code is in strict mode, this is undefined
* Otherwise, this is window (in a browser)
Let's look at how this works in practice:
class Foo {
value = 10;
doSomething() {
// Prints 'undefined', not '10'
console.log(this.value);
}
}
let f = new Foo();
window.setTimeout(f.doSomething, 100);
This code will print undefined (or, in strict mode, throw an exception).
This is because we ended up in the last branch of the decision tree above.
The doSomething function was invoked, the function wasn't a result of a bind call, and it wasn't invoked in a method syntax position.
We can't see the code for setTimeout to see what its invocation looks like, but we don't need to.
Something to realize is that all doSomething methods point to the same function object.
In other words:
let f1 = new Foo();
let f2 = new Foo();
// 'true'
console.log(f1.doSomething === f2.doSomething);
We know that setTimeout can only see the function we passed it, so when it invokes that function,
there's no way for it to know which this to provide.
The this context has been lost due to our referencing the method without invoking it.
The Red Flag
Once you know about this problems, they're easy to spot:
class Foo {
value = 10;
method1() {
doSomething(this.method2); // DANGER, method reference without invocation
}
method2() {
console.log(this.value);
}
}
The Solution
You have a few options here, each with its own trade-offs.
The best option depends on how often the method in question is invoked from differing call sites.
Arrow Function in Class Definition
Instead of using the normal method syntax, use an arrow function to initialize a per-instance member.
class DemonstrateScopingProblems {
private status = "blah";
public run = () => {
// OK
console.log(this.status);
}
}
let d = new DemonstrateScopingProblems();
window.setTimeout(d.run); // OK
Good/bad: This creates an additional closure per method per instance of your class. If this method is usually only used in regular method calls, this is overkill. However, if it's used a lot in callback positions, it's more efficient for the class instance to capture the this context instead of each call site creating a new closure upon invoke.
Good: Impossible for external callers to forget to handle this context
Good: Typesafe in TypeScript
Good: No extra work if the function has parameters
Bad: Derived classes can't call base class methods written this way using super.
Bad: The exact semantics of which methods are "pre-bound" and which aren't create an additional non-typesafe contract between your class and its consumers.
Function Expression at Reference Site
Shown here with some dummy parameters for explanatory reasons:
class DemonstrateScopingProblems {
private status = "blah";
public something() {
console.log(this.status);
}
public run(x: any, y: any) {
// OK
console.log(this.status + ': ' + x + ',' + y);
}
}
let d = new DemonstrateScopingProblems();
// With parameters
someCallback((n, m) => d.run(n, m));
// Without parameters
window.setTimeout(() => d.something(), 100);
Good/bad: Opposite memory/performance trade-off compared to the first method
Good: In TypeScript, this has 100% type safety
Good: Works in ECMAScript 3
Good: You only have to type the instance name once
Bad: You'll have to type the parameters twice
Bad: Doesn't easily work with variadic parameters
I think that you should move the result block into the subscribe callback associated the getPlaces method call:
showPosition(position) {
this.xml_Latitude = position.coords.latitude;
this.xml_Lang = position.coords.longitude;
this._http.getPlaces(this.xml_Latitude, this.xml_Lang).subscribe(
data => {
this.maps = data;
var result = this.maps.results; // <----------
var city = result[0].address_components[4].long_name + "," + result[0].address_components[6].long_name;
alert(city);
},
err => { this.cat_error = true }
);
}
It's because this.maps is undefined before the callback is called. And you try tyo get the result attribute before (this.maps.results).
Edit
I also see a problem at the line navigator.geolocation.getCurrentPosition. You could refactor your code this way:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => { // <----
this.showPosition(position);
});
} else {
alert("Geolocation is not supported by this browser.");
}

How to test, with Mockery, a controller that uses an Ardent Model?

I am trying to test this function in a Laravel controller:
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store()
{
$project = $this->project->create(Input::all());
if ( $errors = $project->errors()->all() ) {
return Redirect::route('projects.create')
->withInput()
->withErrors($errors);
}
return Redirect::route('projects.index')
->with('flash', Lang::get('projects.project_created'));
}
My (wrong) test looks like this:
public function testStoreFails()
{
$this->mock->shouldReceive('create')
->once()
->andReturn(Mockery::mock(array(
false,
'errors' => array()
)));
$this->call('POST', 'projects');
$this->assertRedirectedToRoute('projects.create');
$this->assertSessionHasErrors();
}
The problem is, basically, that Ardent sets $project to false when the validation fails, but it also sets errors on that same object, that can be retreived with ->errors()->all().
I'm quite lost trying to find out what to return in the mock.
Note: The constructor injects the model:
public function __construct(Project $project)
{
$this->project = $project;
$this->beforeFilter('auth');
}
– Edited by following comments in answer –
If I'm understanding what you are trying to do properly, you could do this:
Mock the create() method to return a mocked Project.
Mock the save() method to return false.
Mock a MessageBag instance, which is the object that errors() would return. This mock should have a mocked all() method.
Make the mocked errors() method to return the MessageBag mock.
Assuming $this->mock is a mock of the Project class here:
// given that $this->mock = Mockery::mock('Project')
public function testStoreFails()
{
// Mock the create() method to return itself
$this->mock->shouldReceive('save')->once()->andReturn(false);
// Mock MessageBag and all() method
$errors = Mockery::mock('Illuminate\Support\MessageBag');
$errors->shouldReceive('all')->once()->andReturn(array('foo' => 'bar'));
// Mock errors() method from model and make it return the MessageBag mock
$this->mock->shouldReceive('errors')->andReturn($errors);
// Proceed to make the post call
$this->call('POST', 'projects');
$this->assertRedirectedToRoute('projects.create');
$this->assertSessionHasErrors();
}

When calling a WCF RIA Service method using Invoke, does the return type affect when the Completed callback is executed?

I inherited a Silverlight 5 application. On the server side, it has a DomainContext (service) with a method marked as
[Invoke]
public void DoIt
{
do stuff for 10 seconds here
}
On the client side, it has a ViewModel method containing this:
var q = Context.DoIt(0);
var x=1; var y=2;
q.Completed += (a,b) => DoMore(x,y);
My 2 questions are
1) has DoIt already been activated by the time I attach q.Completed, and
2) does the return type (void) enter into the timing at all?
Now, I know there's another way to call DoIt, namely:
var q = Context.DoIt(0,myCallback);
This leads me to think the two ways of making the call are mutually exclusive.
Although DoIt() is executed on a remote computer, it is best to attach Completed event handler immediately. Otherwise, when the process completes, you might miss out on the callback.
You are correct. The two ways of calling DoIt are mutually exclusive.
If you have complicated logic, you may want to consider using the Bcl Async library. See this blog post.
Using async, your code will look like this:
// Note: you will need the OperationExtensions helper
public async void CallDoItAndDosomething()
{
this.BusyIndicator.IsBusy = true;
await context.DoIt(0).AsTask();
this.BusyIndicator.IsBusy = false;
}
public static class OperationExtensions
{
public static Task<T> AsTask<T>(this T operation)
where T : OperationBase
{
TaskCompletionSource<T> tcs =
new TaskCompletionSource<T>(operation.UserState);
operation.Completed += (sender, e) =>
{
if (operation.HasError && !operation.IsErrorHandled)
{
tcs.TrySetException(operation.Error);
operation.MarkErrorAsHandled();
}
else if (operation.IsCanceled)
{
tcs.TrySetCanceled();
}
else
{
tcs.TrySetResult(operation);
}
};
return tcs.Task;
}
}

jmockit - Mocking chain of methods one of which returns a Collection using #Cascading

I am trying to mock a method call which goes something like this:
rapContext.getSysInfo().get(key)
The getSysInfo() method returns a ConcurrentHashMap.
Here is what I have done:
Class ABCTest {
#Cascading RapContext context;
#Test
doTest() {
new Expectations() {
{
rapContext.getSysInfo().get(anyString);
result = new UserPrefCtxObject();
}
}
}
With this I get a NullPointerException on rapContext.getSysInfo(). Call to getSysInfo() returns null. If I call any other method which does not return a collection, for instance rapContext.getDomain() everything working fine.
I am not sure what I am missing.
Thanks
The code example is not complete however you are likely running into some issue associated with accidentally mocking Map. If a Map (or any part of the Collection framework) is mocked then a lot of things will break. I could not reproduce your problem as any attempt to mock RapContext using #Cascading resulted in a stack over flow.
You could partially mock RapContext instead and then either return a real or mocked Map. When I run into similar issues I generally get around them using either #Injectable to only mock an instance of a class or using partial mocks.
Here is an approach that will let you mock getSysInfo:
public class RapContextTest {
#Injectable ConcurrentHashMap<String, Object> mockedMap;
#Test
public void testContext() {
RapContext context = new RapContext();
new MockUp<RapContext>(){
#Mock
public ConcurrentHashMap getSysInfo(){
return mockedMap;
}
};
new NonStrictExpectations() {
{
mockedMap.get(anyString);
result = "Success";
}
};
Object value = context.getSysInfo().get("test");
System.out.println(value);
}
}