Mock/Fake ASP.NET Service References and QUnit testing - asp.net

I'm just getting into QUnit testing, and have run into a problem on my first page :S
We use ASP.NET Service References to take advantage of async data loading on html pages, creating a reference to a web service in the same project. What ASP.NET does behind the scenes (ScriptManager control) is create a JS file representing the service methods and handling all the AJAX calling.
Using this, I have a page that calls one of these methods in the document.ready jQuery event. I'm now trying to test against this js file using QUnit, but avoid having the js file call the actual web service and use a mock service instead. Here's what I have for an attempt so far:
main.js (production code file)
var Service;
$(document).ready(function () {
//class definition created by asp.net behind the scenes
Service = MyProject.services.DataService;
//the method that calls the service
LoadData();
});
function LoadData() {
Service.GetData(OnGetDataSuccess, OnGetDataFailure);
}
main-test.js (test QUnit code, main.js is referenced in this page)
function SetupMockService(result) {
Service = { "GetData": function (OnSuccess, OnFailure) {
GetDataCalled = true;
OnSuccess(result);
//this is required in an asyncTest callback, I think?
start();
}, "GetDataCalled": false};
}
$(document).ready(function () {
module("LoadData");
asyncTest("LoadData should call GetData from service", function () {
SetupMockService(result);
LoadData();
equals(Service.GetDataCalled, true, "GetData has been called");
});
This test fails. The LoadData method is called as part of the original (main.js) document.ready event, so it still calls the production web service, and the tests fail because that GetDataCalled variable is never set (or defined in production). Am I doing the asyncTest wrong? (This is my first day with QUnit, so I could very well be)
The other way I can see this working is if I can override the main.js document.ready event, but I'm not quite sure on how to do that. I also don't want to add "testEnvironment == true" checks to my production main.js code.

Turns out I had things a bit backwards, as well as one obvious mistake. Here's the resulting code that works
main-tests.js
//the test itself isn't calling async methods, so it doesn't need to use asyncTest
test("LoadData should call GetData from service", function () {
SetupMockService();
LoadData();
equals(Service.GetDataCalled, true, "GetData has been called");
});
function SetupMockService() {
//redefining the Service variable specified in main.js with a mock object
Service = { "GetData": function (OnSuccess, OnFailure) {
//I forgot the "this" part... d'oh!
this.GetDataCalled = true;
}, "GetDataCalled": false
};
}
This still doesn't fix the issue with the original main.js's document.ready code being executed, but I'll figure that out.

Related

Is it possible to stub meteor methods and publications in cypress tests?

Is it possible to stub meteor methods and publications in cypress tests?
In the docs (https://docs.cypress.io/guides/getting-started/testing-your-app#Stubbing-the-server) it says:
This means that instead of resetting the database, or seeding it with
the state we want, you can force the server to respond with whatever
you want it to. (...) and even test all of the edge cases, without needing a server.
But I do not find more details about that. All I can find is, that when not using the virtual user in the tests to fill the database, it is possible to call API calls on the app, like so:
cy.request('POST', '/test/seed/user', { username: 'jane.lane' })
.its('body')
.as('currentUser')
In my opinion that is not a "stub". It is a method to "seed" the database.
Is it possible to tell cypress to answer a meteor method in the client code like
Meteor.call('getUser', { username: 'jane.lane' }, callbackFunction);
with some data, it would give back in production?
I can only show an example using sinon to stub Meteor method calls:
const stub = sinon.stub(Meteor, 'call')
stub.callsFake(function (name, obj, callback) {
if (name === 'getUser' && obj.username === 'jane.lane') {
setTimeout(function () {
callback(/* your fake data here */)
})
}
})
That would be of corse a client-side stub. You could also simply override your Meteor method for this one test.

How do I reliably pull data from Meteor server collections to client collections when using an existing mongodb as MONGO_URL?

I know that there are several methods to share collections on both the client and server -- namely either in top level lib folder or publish/subscribe model -- but when I try either of these things when using mongodb running at localhost:27017 as my MONGO_URL, I am not reliably getting data on the client. Occasionally console.log(myCollection.findOne({})) will return expected data in the browser but most of the time it returns undefined.
//Client side code
Template.controls.onCreated(function controlsOnCreated() {
Meteor.subscribe("myEvents");
Events = new Mongo.Collection("events");
});
//Server side code
Meteor.startup(() => {
Events = new Mongo.Collection("events");
}
Meteor.publish('myEvents', function() {
console.log(Events.find());
return Events.find();
});
UPDATED CODE -- returns Events on server but not client:
//Client
Template.controls.onCreated(function controlsOnCreated() {
this.subscribe("myEvents");
});
//Server
if (Meteor.isServer) {
Meteor.publish("myEvents", function() {
return Events.find();
});
}
// /collections/events.js
Events = new Mongo.Collection("events");
UPDATE 2:
I am attempting to verify the publication in the browser after the page has rendered, calling Events.findOne({}) in the Chrome dev tools console.
on your client:
Template.controls.onCreated(function controlsOnCreated() {
Meteor.subscribe("myEvents");
Events = new Mongo.Collection("events");
});
that is an odd place to define the Events variable. typically, you would put that line of code in a JS file common to both platform. e.g.
collections/events.js:
Events = new Mongo.Collection("events");
when that line runs on the server, it defines the mongo collection and creates a server-side reference to it. when it runs on the client, it creates a collection by that name in mini-mongo and creates a client-side reference to it.
you can write your onCreated like this (note "this" instead of "Meteor"):
Template.controls.onCreated(function() {
this.subscribe("myEvents");
});
you don't say where on the client you ran your console.log with the find(). if you did it in the onCreated(), that's too early. you're seeing the effects of a race condition. typically, you might use it in a helper:
Template.controls.helpers({
events() {
return Events.find({});
}
});
and display the data in the view:
{{#each event in events}}
{{event.name}}
{{/each}}
that helper will run reactively once the data from the publish shows up.

How to integrate ASP.NET 5 with signalr

I'm trying to make an ASP.NET 5 web app using SignalR. I've created an empty web app, I've added an index.html page with some contents but I can't integrate SignalR. So far, in order to use SignaR, I've carried out the following step:
1) I've added the following dependency in the project.json file:
"Microsoft.AspNet.SignalR.Server": "3.0.0-rc1-final"
2) In the Startup class, I've added
services.AddSignalR();
to the ConfigureServices method, and
app.UseSignalR();
to the Configure method.
3) I've added to the project a class deriving from Microsoft.AspNet.SignalR.Hub and inserted a public method into it.
After this, in index.html I've added the logic trying to access the method but when I launch the app firefox console says
Error: $ is not defined
Here is the javascript code:
<script>
var app = angular.module("myapp", []);
app.controller("myctr", function ($scope) {
$scope.input = "";
$scope.output = "";
$.connection.myHub1.client.JSMet1 = function (x) {
$scope.$apply(function () {
$scope.output = x;
});
};
$scope.AggiornaTesto = function () {
$.connection.myHub1.server.cSMet1($scope.input);
};
$.connection.hub.start();
});
</script>
As you can see I also use AngulaJS.
Error: $ is not defined
Its because you haven't added jQuery reference to your page, please read this article from Microsoft, it will step by step demonstrate you how to implement SingnalR with ASP.NET MVC projects.

meteor.call does not call method from meteor.method

i have an issue with callback method.
i have created on methods.js in server folder
and one callback.js file in client/test/mytest folder.
my callback.js contains following code
Template.testHello.events({
"click #testHello": function(e) {
Meteor.call("testmethod",function(error, id) {
if (error) {
Errors.throwError(error.reason);
}
return false;
});
return false;
}
});
and methods.js file code is
Meteor.methods({
testmethod: function(att) {
alert("hello testmethod..");
}
});
but when i clicked on button "testHello" then it gives me error like "internal server error 500".
can anyone have idea about this?
Thanks,
It makes no sense to have client-only method calls because Meteor methods are intended to perform RMI (remote method invokation) on the server.
Move your methods.js to either server/methods.js or lib/methods.js if you want your method to have a simulation counterpart on the client.
EDIT :
As hinted by #user728291, the alert method is defined on the window object which is a browser related object thus only available on client environment, you can use console.log instead to print something on the server.

Signalr (1.0.0-alpha2) Hubs - Can you add client functions after connection has been started?

Using Signalr (1.0.0-alpha2), I want to know if it is possible to add client functions after a connection has been started.
Say I create my connection and grab the proxy. Then I add some Server Fired client functions to the hub to do a few things. Then I start my connection. I then want to add some more Server Fired functions to my hub object. Is this possible?
var myHub= $.connection.myHub;
myHub.SomeClientFunction = function() {
alert("serverside called 'Clients.SomeClientFunction()'");
};
$.connection.hub.start()
.done(function() {
myHub.SomeNewClientFunction = function() {
alert("serverside called 'Clients.SomeNewClientFunction()'");
}
})
This example is not realistic, but I basically want to send my 'myHub' variable to a different object after the hub is started to subscribe to new events that the original code did not care for.
Real Life Example: A dashboard with a number of different hub events (new site visits, chat message, site error). I 'subscribe' after the connection has started and then pass my hub proxy to all of my different UI components to handle their specific 'message types'. Should I create separate Hubs for these or should I be able to add more Server Fired client functions on the fly?
Yes you can. Use the .on method.
Example:
myHub.on('somethingNew', function() {
alert("This was called after the connection started!");
});
If you want to remove it later on use the .off method.
I have the exact same situation. You might want to consider adding another layout of abstraction if you're trying to call it from multiple places.
Here's a preliminary version of what I've come up with (typescript).
I'll start with the usage. SignalRManager is my 'manager' class that abstracts my debuggingHub hub. I have a client method fooChanged that is triggered on the server.
Somewhere in the module that is using SignalR I just call the start method, which is not re-started if already started.
// ensure signalR is started
SignalRManager.start().done(() =>
{
$.connection.debuggingHub.server.init();
});
Your 'module' simply registers its callback through the manager class and whenever the SignalR client method is triggered your handler is called.
// handler for foo changed
SignalRManager.onFooChanged((guid: string) =>
{
if (this.currentSession().guid == guid)
{
alert('changed');
}
});
This is a simple version of SignalRManager that uses jQuery $.Callbacks to pass on the request to as many modules as you have. Of course you could use any mechanism you wanted, but this seems to be the simplest.
module RR
{
export class SignalRManager
{
// the original promise returned when calling hub.Start
static _start: JQueryPromise<any>;
private static _fooChangedCallback = $.Callbacks();
// add callback for 'fooChanged' callback
static onfooChanged(callback: (guid: string) => any)
{
SignalRManager._fooChangedCallback.add(callback);
}
static start(): JQueryPromise<any>
{
if (!SignalRManager._start)
{
// callback for fooChanged
$.connection.debuggingHub.client.fooChanged = (guid: string) =>
{
console.log('foo Changed ' + guid);
SignalRManager._fooChangedCallback.fire.apply(arguments);
};
// start hub and save the promise returned
SignalRManager._start = $.connection.hub.start().done(() =>
{
console.log('Signal R initialized');
});
}
return SignalRManager._start;
}
}
}
Note: there may be extra work involved to handle disconnections or connections lost.

Resources