Flutter testing, passing an actual HTTP client, instead of a mocked one - flutter

I have developed a package in flutter, and wanted to test it, which makes a network call.
As we know that all network request while testing will return 404, and such HTTP reqeust needs to be mocked.
However its also possible to use the orginal HTTP clients instead of mocking or getting 404.
https://github.com/flutter/flutter/issues/19086#issuecomment-402639134
How do we do that ?
I have tried this :
main(){
TestWidgetsFlutterBinding.ensureInitialized();
HttpOverrides.runZoned(() {
test("Case1: Make HTTP request to an actual server", ()async{
let a = MyPackage.makesAHTTPRequest();
expect(a,"hello world");
});
}, createHttpClient: (SecurityContext c) => new HttpClient(context: c));
}
My URL is working all fine.
But it keeps giving me 404.
How do one use real HTTP client, if needed that way?

Ok so if any one is facing a similar issue like me use this hack.
You will need to modify your class, in a way that we can inject HTTP clients into it at run time. We will need to modify our test case as such.
import 'package:http/http.dart'; //client is from this pack
Client httpclinet = Client();
var a = MyPackage.makesAHTTPRequest(httpclient);
remove that Httpoverride.runzoned cod, you can pass Client object from http package directly.
Some test case will fail, due to fake asynchronous effect, but you can use timeouts to manage those.
You will also need to remove any such statements:
TestWidgetsFlutterBinding.ensureInitialized();
In my case I added this line as I was loading files from assets, using packages notation, I referenced them locally and removed above ensureInitalized line as well. [Actually I passed flag to use local notation during testing and package notation otherwise]

Related

ServiceStack JsonServiceClient: SendAsync uses wrong path, ignores Route attribute?

I am using JsonServiceClient in a Xamarin app, like this:
JsonServiceClient client = new JsonServiceClient("https://my.domain.com/");
SetHeaders(client);
var request = ...; // this is IRequest<T>
var result = await client.SendAsync(request); // <-- FAILS, can't find service
My backend returns an answer, saying that there is no service at that endpoint, which is true, the path that was actually sent over the wire is incorrect.
The request is defined in a lib, like so:
[Route("/mybasepath/endpoint", "POST")]
public class Login : IReturn<LoginResponse>
{
}
The problem is the path that is used in the call, which is wrong and does not follow the Route attribute:
https://my.domain.com/json/reply/Login
Here, ServiceStack client uses the default /json/reply path, even though I have the Route attribute defined in the DTO.
If I change the method used on the client instance, and instead use PostAsync, the path is ten correct and the call work as expected:
JsonServiceClient client = new JsonServiceClient("https://my.domain.com/");
SetHeaders(client);
var request = ...; // this is IRequest<T>
var result = await client.PostAsync(request); // <-- WORKS!
I don't have a minimal project right now that can be immediately tested, maybe it is something easy I have missed?
(Using ServiceStack.Client v 5.10.4 on VS 2019 16.9)
If you want to use ServiceStack's generic Send* APIs the Service Clients needs to explicitly infer the Verb to use by annotating the Request DTO with an HTTP Verb Interface Marker, not necessary for AutoQuery or AutoQuery CRUD APIs which is inferred from their base classes.
Otherwise Send* APIs are designed to fallback to use ServiceStack's pre-defined Routes.

What are the modifications needed to be done to get Wiremock running?

I have a .Net Core web API solution called ReportService, which calls another API endpoint (we can call this PayrollService) to get payroll reports. So my requirement is to mock the PayrollService using Wiremock.Net.
Also currently I have a automation test case written, which will directly call the ReportService controller and will execute all the service logic, and also classes which calls PayrollService and the DB layer logic and will get the HTTP result back from the ReportService.
Please note that the Automation test cases is a separate solution. So my requirement is to run the automation test cases like before on ReportService, and the payroll service will be mocked by Wiremock.
So, what are the changes that need to happen in the codebase? Do we have to change the url of the ReportService to be the Wiremock server base url in the ReportService solution? Please let us know, and please use the terms I have used in the question regarding the project names so I am clear.
Your assumption is indeed correct, you have make the base URL which is used by ReportService configurable.
So that for your unit / integration tests you can provide the URL on which the WireMock.Net server is running.
Example:
[Test]
public async Task ReportService_Should_Call_External_API_And_Get_Report()
{
// Arrange (start WireMock.Net server)
var server = WireMockServer.Start();
// Setup your mapping
server
.Given(Request.Create().WithPath("/foo").UsingGet())
.RespondWith(
Response.Create()
.WithStatusCode(200)
.WithBody(#"{ ""msg"": ""Hello world!"" }")
);
// Act (configure your ReportService to connect to the URL where WireMock.Net is running)
var reportService = new ReportService(server.Urls[0]});
var response = reportService.GetResport();
// Assert
Assert.Equal(response, ...); // Verify
}

Flutter call http requests from a unit\widget test without mocking

I have an app that follows MVC+service architecture. The service layer makes the http requests for rest APIs. However the response of the http requests change intermittently which cause my models to change or else random crashes in my app. SO to capture the change in these APIs I want to write some automated tests which can tell me exactly what changed. A sample test case is as following:
test("login_valid", () async {
final loginData = LoginData(
email: "abc#gmail.com",
password: "123"
);
final parameters = loginData.toJson();
var json = await httpService.post(parameters);
var loginResponse = LoginResponse.fromJson(json);
expect(loginResponse.status, "OK");
});
However, the above code throws SocketException upon run. I know this exception is thrown when INTERNET permission is not given in AndroidManifest.xml but I don't know how to set this for unit\widget tests.
P.S. I can't mock the service layer using mockit or similar framework because the whole point is to test my service layer which doesn't have any business logic but just provides network integration.
Any solution or suggestion will be really helpful. I am okay with other approaches to achieve the same intent also, if there are any.
Check this or the gihub issue discussion pointed there
https://timm.preetz.name/articles/http-request-flutter-test

Restangular - how to cancel/implement my own request

I found a few examples of using fullRequestInterceptor and httpConfig.timeout to allow canceling requests in restangular.
example 1 | example 2
this is how I'm adding the interceptor:
app.run(function (Restangular, $q) {
Restangular.addFullRequestInterceptor(function (element, operation, what, url, headers, params, httpConfig) {
I managed to abort the request by putting a resolved promise in timeout (results in an error being logged and the request goes out but is canceled), which is not what I want.
What I'm trying to do - I want to make the AJAX request myself with my own requests and pass the result back to whatever component that used Restangular. Is this possible?
I've been looking a restangular way to solve it, but I should have been looking for an angular way :)
Overriding dependency at runtime in AngularJS
Looks like you can extend $http before it ever gets to Restangular. I haven't tried it yet, but it looks like it would fit my needs 100%.
I'm using requestInterceptor a lot, but only to change parameters and headers of my request.
Basically addFullRequestInterceptor is helping you making change on your request before sending it. So why not changing the url you want to call ?
There is the httpConfig object that you can modify and return, and if it's close to the config of $http (and I bet it is) you can change the url and even method, and so change the original request to another one, entirely knew.
After that you don't need timeout only returning an httpConfig customise to your need.
RestangularConfigurer.addFullRequestInterceptor(function (element, operation, route, url, headers, params, httpConfig) {
httpConfig.url = "http://google.com";
httpConfig.method = "GET";
httpConfig.params = "";
return {
httpConfig: httpConfig
};
});
It will be pass on and your service or controller won't know that something change, that's the principle of interceptor, it allow you to change stuff and returning to be use by the next process a bit like a middleware. And so it will be transparent to the one making the call but the call will be made to what you want.

Karma/Jasmine Unit Testing an AngularJS Service with Dependencies

I'm a novice programmer who is very new to both AngularJS and the practice of unit testing. I've spent hours trying to find the solution to this but I'm becoming increasingly more confused. If anyone could point me in the right direction I would greatly appreciate it. I'll try to be as descriptive as possible.
The situation is this:
I have created a service in AngularJS (Service A) that has a couple of functions. Each of these functions makes an $http GET request to a REST API and returns an $http promise object that contains JSON data. Within these functions, the URL is constructed through the implementation of another very simple service (Service B) that has been injected as a dependency into Service A. I have created a mock of Service B to isolate it from all of its dependencies. Both of these services are defined inside of the same module named "services". In this case, there is no real need for this dependency but I just want to understand how it works.
Using Jasmine, I would like to construct a unit test for Service A to ensure that the requests it is making to the API are constructed correctly and possibly if the correct JSON data is being returned. At the same time, I do not want any real API calls to be made.
This is what I know:
$httpBackend mock is what I need to be able to make fake calls to the API and it provides functionality to expect certain requests and return specified results.
I need to test the real Service A and inject the mock I've created of Service B. I know there are ways to do this using Jasmine Spies and $provide. I've also seen examples where sinon.js is used, and I'm not sure which is the best approach.
I will post my source code below, which is written in CoffeeScript.
Service A:
'use strict'
angular.module("services")
.service("ServiceA", ["$http", "ServiceB", ($http, ServiceB) ->
#Uses underscore.js to set this default attribute
defaults = withCredentials:true
getVarset: (itemName, options={}) ->
options.method = "GET"
options.url = ServiceB.makeUrl("item/#{itemName}")
$http _.defaults(options, defaults)
getVarsets: (options = {}) ->
options.method = "GET"
options.url = ServiceB.makeUrl("items")
$http _.defaults(options, defaults)
getModelsForVarset: (itemName, options = {}) ->
options.method = "GET"
options.url = ServiceB.makeUrl("item/#{itemName}/prices")
$http _.defaults(options, defaults)
])
Service B:
'use strict'
angular.module('services')
.service 'ServiceB', [ ->
# Just return the string
# This service builds the real URL, but I've removed this
makeUrl: (Url) ->
"#{Url}"
]
so are you saying that you know how to do this with $provide/Jasmine spies and are looking for alternatives? I've mostly just used the $provide/spy method for mocking and it's worked out really well for me so far.
something like:
beforeEach(function() {
// set up a default value for your mock
bMock = {
makeUrl: jasmine.createSpy('makeUrl() mock').andReturn('http://www....')
}
// use the $provide service to replace ServiceB
// with your mock
module('services', function($provide) {
$provide.value('ServiceB', bMock);
});
});
it('should do what its supposed to do', function() {
// test...
});
then, if you want to use $httpBackend to mock the http requests in service A, you just need to use the $injector service to grab $httpBackend, then call .when(...) on it to set things up, a la http://docs.angularjs.org/api/ngMock.$httpBackend