Do I need different module in each test case using Nancy? - selenium

I want to test my web application, without the web services layer. in order to do that I am using Nancy framework.
I am mocking ServiceA as follow:
public class ServiceAModule : NancyModule
{
public ServiceAModule () : base("/serviceAPath")
{
Get["/"] = p =>
{
var s = #"{Property1 : 23}";
var jsonBytes = Encoding.UTF8.GetBytes(s);
return new Response
{
ContentType = "application/json",
Contents = stream => stream.Write(jsonBytes, 0, jsonBytes.Length),
StatusCode = HttpStatusCode.OK
};
};
}
Now, in my tests: I initialize Nancy service:
private static IDisposable CreateService()
{
const string url = "http://+:8088";
var service = WebApp.Start(url, builder =>
{
var browser = new Browser(with => { with.EnableAutoRegistration(); });
builder.UseNancy();
});
return service;
}
And I am testing the application UI using selenium.
My question is: I need different scenario (different response from ServiceAModule Get end point), what are my options?
As I see it, I have one option, which is to create different module for each test case and register this module on each test.
This solution brings a lot of code, and mess.
Do I have any other option? what is the common use of Nancy in this cases?
Thank you!

What do you mean by different response ? You can add as many actions as you want on the same Module
public ServiceAModule () : base("/serviceAPath")
{
Get["/"] = p => Response.AsJson(new {Property1 : 23 });
Post["/"] = p => Response.AsText("Saved !!!");
Get["/thing"] = p => Response.AsJson(new { foo : 3434 , bar : 900 });
}

Related

RavenDB The database **** is currently locked because Checking if we need to recreate indexes

I am using RavenTestDriver for my unit tests .
Here is my configuration of my test :
ConfigureServer(new TestServerOptions
{
CommandLineArgs = new System.Collections.Generic.List<string> { "--RunInMemory=true", },
FrameworkVersion = null,
}) ;
IDocumentStore store = GetDocumentStore();
try
{
store.Maintenance.ForDatabase(ravenTestDatabaseName).Send(new GetStatisticsOperation());
}
catch (DatabaseDoesNotExistException)
{
store.Maintenance.Server.Send(new CreateDatabaseOperation(new DatabaseRecord(ravenTestDatabaseName)));
}
session = store.OpenAsyncSession(new SessionOptions()
{
Database=ravenTestDatabaseName,
});
var hostBuilder = easy.api.Program.CreateHostBuilder(new string[0])
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder.UseTestServer();
})
.ConfigureServices(services =>
{
services.AddScoped<ICurrentUserService, InitRequest>();
services.AddScoped<ICacheStorage>(provider=>
{
return new Mock<ICacheStorage>().Object;
});
services.AddRavenDbAsyncSession(GetDocumentStore(new GetDocumentStoreOptions()));
services.AddScoped<IAsyncDocumentSession>((c) =>
{
return store.OpenAsyncSession(new SessionOptions()
{
Database=ravenTestDatabaseName,
});
});
});
So I have several test in my solution:
When I run each test individual the test passed .But When I run all tests together I get this error :
Raven.Client.Exceptions.Database.DatabaseDisabledException : Raven.Client.Exceptions.Database.DatabaseDisabledException: The database Test-Server is currently locked because Checking if we need to recreate indexes
at Raven.Server.Documents.DatabasesLandlord.UnloadAndLockDatabase(String dbName, String reason) in C:\Builds\RavenDB-Stable-5.2\52000\src\Raven.Server\Documents\DatabasesLandlord.cs:line 907
at Raven.Server.Web.System.AdminDatabasesHandler.Put() in C:\Builds\RavenDB-Stable-5.2\52000\src\Raven.Server\Web\System\AdminDatabasesHandler.cs:line 327
at Raven.Server.Routing.RequestRouter.HandlePath(RequestHandlerContext reqCtx) in C:\Builds\RavenDB-Stable-5.2\52000\src\Raven.Server\Routing\RequestRouter.cs:line 349
at Raven.Server.RavenServerStartup.RequestHandler(HttpContext context) in C:\Builds\RavenDB-Stable-5.2\52000\src\Raven.Server\RavenServerStartup.cs:line 174
The server at /admin/databases?name=Test-Server&replicationFactor=1&raft-request-id=d9a39f56-a1ad-44b7-b6e5-b195c1143c4b responded with status code: ServiceUnavailable.
I just leave the database name empty .
public class TestHostBuilder : RavenTestDriver, IAsyncLifetime
{
public HttpClient httpClient = null;
public IDocumentStore documentStore = null;
public async Task InitializeAsync()
{
documentStore = GetDocumentStore();
var hostBuilder = easy.api.Program.CreateHostBuilder(new string[0])
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder.UseTestServer();
})
.ConfigureServices(services =>
{
services.AddScoped<ICurrentUserService, InitRequest>();
services.AddScoped<ICacheStorage>(provider =>
{
return new Mock<ICacheStorage>().Object;
});
services.AddRavenDbAsyncSession(GetDocumentStore(new GetDocumentStoreOptions()));
services.AddTransient<IAsyncDocumentSession>((c) =>
{
return documentStore.OpenAsyncSession();
});
});
var host = hostBuilder.Start();
httpClient = host.GetTestClient();
}
public Task DisposeAsync()=> Task.CompletedTask;
}

Logging from inside a Customized Bad Request Response

I'm capturing model validation errors using the code below and outputting a custom 400 response from the CustomProblemDetails object which works great. My question is, I want to log from within the CustomProblemDetails object but don't see how I can use DI. I've passed in context which gives me access to the services but is this the way to go? If so it appears I can only get access to the ILoggerFactory how do I log using ILoggerFactory?
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var problemDetails = new CustomProblemDetails(context)
{
Type = "https://contoso.com/probs/modelvalidation",
Title = "One or more model validation errors occurred.",
Status = StatusCodes.Status400BadRequest,
Detail = "See the errors property for details.",
Instance = context.HttpContext.Request.Path
};
return new BadRequestObjectResult(problemDetails)
{
ContentTypes = { "application/problem+json" }
};
};
});
For logging in InvalidModelStateResponseFactory, you could try code like:
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("Logger From Invalid Model");
var problemDetails = new CustomProblemDetails(context)
{
Type = "https://contoso.com/probs/modelvalidation",
Title = "One or more model validation errors occurred.",
Status = StatusCodes.Status400BadRequest,
Detail = "See the errors property for details.",
Instance = context.HttpContext.Request.Path
};
logger.LogError(JsonConvert.SerializeObject(problemDetails));
return new BadRequestObjectResult(problemDetails)
{
ContentTypes = { "application/problem+json" }
};
};
});

Using API tags for a library

I'm currently creating a library for an API. The endpoints have optional tags, and so I'm trying to create a way to use them in the functions.
import * as request from "request";
class Api {
key: string;
constructor(userInput: string) {
this.key = userInput;
}
champions(tags: object) {
Object.keys(tags).forEach(function(key) {
console.log(key + " = " + tags[key])
})
request(`https://api.champion.gg/v2/champions?api_key=${this.key}&${tags}`, function (error, response, body) {
if(!error && response.statusCode == 200) {
let info = JSON.parse(body)
}
});
}
}
var test = new Api("key")
test.champions({"champData": ["kda", "damage"], "rawr": ["xd", "lmao"]})
So far, the combining of Object.keys and forEach has allowed me to get the response of champData=kda,damage and rawr=xd,lmao, however, I need to be able to assign these to a variable that's usable in the URL. How can I get this to work?
Another issue that may occur later on is that, between each tag, there needs to be an & symbol, but not at the end. I apologize for throwing multiple problems into one, but because this is my first experience with something like this, I'm having many issues.
You can use Object.entries() and URLSearchParams()
const tags = {a:1, b:2, c:3};
const params = new URLSearchParams();
const key = "def";
Object.entries(tags).forEach(([key, prop]) => params.set(key, prop));
const url = `https://api.champion.gg/v2/champions?api_key=${key}&${params.toString()}`;
console.log(url);

Logs for Protractor

I am new to protractor and want to create logs for my test cases. I used if and else to write logs. I wanted to know if there is any better way of writing logs for protractor test cases?
My Code:
var colors = require('colors/safe');
var mapFeedBackpage=require('./mapFeedBack-page.js')
describe("Map feedback Automation",function()
{
var mapFeedBack= new mapFeedBackpage();
it("Check if the Url works ",function() //spec1
{
console.log("Checking the url :"+browser.params.url+'\n')
browser.get(browser.params.url);
browser.getCurrentUrl().then(function(value){
if(/report/.test(value) === false) {
fail("Result: URL doesnt works-FAIL \n");
}
else
{
console.log(colors.green("PASS :")+browser.params.url+ "is reachable \n");
}
});
});
it("test browser should reach report road option",function() //spec2s
{
console.log("Checking if road report option is available \n");
mapFeedBack.REPORT_ROAD.click();
expect(browser.getCurrentUrl()).toContain("report_road");
browser.getCurrentUrl().then(function(value){
if(/report_road/.test(value) === false) {
fail("Result: URL doesnt works-FAIL");
}
else
{
console.log(colors.green("PASS")+" Road report option is available");
}
});
});
Yes, you can use https://www.npmjs.com/package/log4js which is basically log4j module for nodejs apps. Since protractor is nodejs program it would certainly support this. It's very easy to implement this-
var log4js = require('log4js');
var logger = log4js.getLogger();
logger.debug("Some debug messages");
or you could write a custom logger:
var logger = exports;
logger.debugLevel = 'warn';
logger.log = function(level, message) {
var levels = ['error', 'warn', 'info'];
if (levels.indexOf(level) >= levels.indexOf(logger.debugLevel) ) {
if (typeof message !== 'string') {
message = JSON.stringify(message);
};
console.log(level+': '+message);
}
}
and then use this in your scripts as :
var logger = require('./logger');
logger.debugLevel = 'warn';
logger.log('info', 'Everything started properly.');
logger.log('warn', 'Running out of memory...');
logger.log('error', { error: 'flagrant'});

Angular http testing

I have a fairly simple controller that gets a simple json list of objects ...
function ProductGroupsCtrl($scope, $http, $routeParams, sharedService, popupService) {
$scope.list = null;
$scope.selectedItem = null;
$scope.selectedItemJsonString = '';
$scope.selectItem = function (item) {
$scope.selectedItem = item;
$scope.selectedItemJsonString = JSON.stringify(item);
//alert(JSON.stringify(item));
};
$scope.closePopup = function () {
$scope.selectedItem = null;
$scope.selectedItemJsonString = '';
};
// sharedService.prepForBroadcast($routeParams.anotherVar);
$http({
method: 'GET',
url: '/ProductGroup'
}).success(function (data) {
$scope.list = data;
}).
error(function (data) {
$scope.message = 'There was an error with the data request.';
});
}
I then try to mock the request in the test class:
var scope, ctrl, $httpBackend, sharedServiceMock = {}, popupServiceMock = {};
beforeEach(inject(function (_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$httsypBackend.expectGET('/ProductGroup').
respond([{
ProductGroupID: 5,
MenuTitle: "Promotional Products",
AlternativeText: "Coming soon - a collection of environmentally friendly Promotional Products",
OrdinalPosition: 5,
Active: false
}]);
scope = $rootScope.$new();
ctrl = $controller(ProductGroupsCtrl, {
$scope: scope,
$http: $httpBackend,
sharedService: sharedServiceMock,
popupService: popupServiceMock
});}));
However I receive an error in the testacular window object undefined. What have I done wrong here?
Found the answer. If I remove the error callback function from the $http.get method then it works, i.e. remove the following ...
error(function (data) {
$scope.message = 'There was an error with the data request.';
}
I have to say Angular sure is a steep learning curve for someone who is not a day to day JavaScript programmer (although I seem to be doing more and more). Thanks for the help anyway KatieK :-)