I'm using #types/xrm and attempting to test a method call with sinon. Unfortunately I am hitting quite a few issues due to the complex nature of the return and call I need to mock. I can find really simple examples of sinon stubbing or spying on calls, but nothing more complex than that.
I have the following simple code:
export class AccountForm {
static Fields = class {
static PrimaryContact = "primarycontactid";
static Name = "name";
}
public onLoad(context: Xrm.Events.EventContext): void {
// Get the form context
const formContext = context.getFormContext();
// Get the attributes required
const primaryContact = formContext.getAttribute(AccountForm.Fields.PrimaryContact);
const name = formContext.getAttribute(AccountForm.Fields.Name);
// Add our onchange events
primaryContact.addOnChange(this.onChangePrimaryContact);
name.addOnChange(this.onChangeName);
}
public async onChangePrimaryContact(context: Xrm.Events.EventContext): Promise<void> {
alert("Do something");
}
public async onChangeName(context: Xrm.Events.EventContext): Promise<void> {
alert("Do something else");
}
}
I want to test that an onchange event has been registered to both fields. Ideally, I'd like to check it's the RIGHT onchange, but I'll settle with the fact that it's been called once.
The "easy" way has been to check that the addOnChange method was called twice, this is as below:
import {AttributeMock, XrmMockGenerator} from "xrm-mock";
import * as sinon from "sinon";
import { AccountForm } from "../../src/entities/Account/Form";
describe("Account Form Tests", () => {
describe("Check Onload", () => {
beforeEach(() => {
XrmMockGenerator.initialise();
XrmMockGenerator.Attribute.createString("name", "");
XrmMockGenerator.Attribute.createLookup("primarycontactid", []);
});
it("should register onChange functions", () => {
// Arrange
let formContext = XrmMockGenerator.getFormContext();
let context = XrmMockGenerator.getEventContext();
// Stub
const attributeStub = sinon.stub(AttributeMock.prototype, "addOnChange");
// Act
let form = new AccountForm();
form.onLoad(context);
// Assert
expect(attributeStub.calledTwice).toBeTruthy();
});
});
});
But this is not very resilient, as it is not checking WHICH attributes the onChange functions were added to, or what function was registered.
I've tried stubbing the ForContext's "GetAttribute", but looks like it's requiring me to mock the entire return object, as otherwise, the stub does not return anything? I can get around this with using spy, but still can't work out how to check the attribute that the onChange is being added to and what the function is
Am I missing something obvious here?
Related
I am running some test in my first smart contract called Inbox.sol.
I want to call the method .message in order to see if it is equal to the default variable I use when I deploy the contract with web3.
My Inbox.sol code
pragma solidity >=0.6.12;
contract Inbox {
string public message;
function initialInbox(string memory initialMessage) public {
message = initialMessage;
}
function setMessage(string memory newMessage) public {
message = newMessage;
}
}
My test file with Mocha is:
let accounts
let inbox
beforeEach(async () => {
// Get a list of all accounts
accounts = await web3.eth.getAccounts()
// Use one of those account to deploy the contract
inbox = await new web3.eth.Contract(abi)
.deploy({ data: bytecode, arguments: [INITIAL_ARGUMENT]})
.send({ from: accounts[0], gas: GAS})
})
describe(('Inbox'), () => {
it("has a default message", async () => {
const message = await inbox.methods.message() // Returns a big object
console.log(message)
const messageCalled = await inbox.methods.message().call()
console.log(messageCalled) // Returns nothing
assert.strictEqual(message, INITIAL_ARGUMENT)
})
})
Your JS inbox variable is an instance of web3.eth.Contract (docs).
The inbox.methods.message() is an instance of a helper object (that's the "Returns a big object" in your comment) containing the .call() method, that you need to use when you want to perform a read-only call.
const message = await inbox.methods.message().call() // Returns the string
Docs: https://web3js.readthedocs.io/en/v1.3.4/web3-eth-contract.html#methods-mymethod-call
Change your code and add a contractor.
function constructor(string memory initialMessage) public {
message = initialMessage;
}
if you are using an older version of solidity for example ^0.4.17. you can create a constructor by create a method same name as your contract class.
function Inbox(string memory initialMessage) public {
message = initialMessage;
}
I'm using Akavache's GetAndFetchLatest method and I have created dependency services to communicate with Akavache's method. I'm calling akavache from service layer successfully when i directly reference. For subscribing
MyMod result = null;
var cache = BlobCache.LocalMachine;
var cachedPostsPromise = cache.GetAndFetchLatest(
"mykey",
() => GetInfo(),
offset =>
{
//some condition
});
cachedPostsPromise.Subscribe(subscribedPosts => {
Device.BeginInvokeOnMainThread(() =>
{
//do sothing.
});
});
result = await cachedPostsPromise.FirstOrDefaultAsync();
return result;
It works.But how an I call subscribe on service layer with interface/dependency service?
I think you are new to reactive programming. Understanding the basic principles helps when using Akavache. Maybe this intro helps.
To answer your question, place code like this in your "repository" class:
public override IObservable<MyClass> Get(string key)
{
var cachedObservable = blobCache.GetAndFetchLatest<MyClass>(key,
() => GetFromServerAsync(key));
return cachedObservable ;
}
And in the caller:
private void getNewData()
{
var myClassObservable = myRepository.Get("the key");
myClassObservable.Subscribe(handleNewMyClass);
}
private void handleNewMyClass(MyClass newClass)
{
//handle the new class
}
Note that handleNewMyClass() is called twice:
first with the MyClass from cache
then with the MyClass that was fetched (from the server)
Using this approach you can simply place the repository class in your IoC Container.
Please find the the sample code :
var result = BlobCache.LocalMachine;
var cachedPostsPromise = cache.GetAndFetchLatest(
"mykey",
() => ViewModelLocator.GetInstance<IYourServiceName>().MethodName(),
offset =>
{
//some condition
});
cachedPostsPromise.Subscribe(subscribedPosts => {
Device.BeginInvokeOnMainThread(() =>
{
//Your piece of code
});
});
result = await cachedPostsPromise.FirstOrDefaultAsync();
return result;
Please note the there any anything inside subscribed will be called twice : first set of data will be cache and second set will be freshly fetched from server.You have to manage according.
I'm new to using flux and have started using the alt.js implmentation. I'm wondering when I would use dispatch from within my actions. For example, take this code.
//ImageActions.js
class ImageActions {
getImages(id) {
return Api.get(`topics/${id}`).then(response => {
let images = response.data.filter(image => {
return !image.is_album;
});
this.updateImages(images);
});
}
updateImages(images) {
return images;
}
}
---------------------------------------------------
//ImageStore.js
class ImageStore {
constructor() {
this.images = [];
this.image = {};
this.bindListeners({
handleUpdateImages: ImageActions.UPDATE_IMAGES
});
}
handleUpdateImages(images) {
this.images = images;
}
}
Currently this works without using the dispatch() function as seen in their tutorial here http://alt.js.org/guide/async/
I'm wondering when I'd want to do this and what dispatch does and what it does differently than just returning the value from the updateImages function in ImageaActions.js
You use dispatch when your async calls resolve. In this case it works because when your sync call finishes, you are calling another action (updateImages) which is triggering the dispatch, since getImages is not triggering a dispatch. Remember the return of an async call is a Promise.
How do I tell ReactiveUI to update bindings?
Normally, I would do something like this:
string _instructorNameInput;
public string InstructorNameInput
{
get { return _instructorNameInput; }
set
{
this.RaiseAndSetIfChanged(ref _instructorNameInput, value);
Submit.RaiseCanExecuteChanged();
}
}
However, the following isn't supported:
Submit.RaiseCanExecuteChanged();
As a result, how can I force bindings to update based on the CanExecute predicate that my command relies on?
Updated:
public partial class FormViewModel : ReactiveObject
{
public FormViewModel()
{
Submit = ReactiveCommand.Create(this.WhenAnyValue(x => x.CanSubmit));
Submit.Subscribe(x => OnSubmit());
}
bool _canExecute;
public bool CanSubmit
{
get { return !GetUnsatisfied().Any(); }
set { this.RaiseAndSetIfChanged(ref _canExecute, value); } // Need to update view based on command.CanExecute state change
}
void OnSubmit()
{
var rosterInfo = new RosterInfo(new Course(CourseInput.Name),
new Instructor(InstructorNameInput, InstructorIdInput));
var repository = GetRepository();
repository.AddCourseInfo(rosterInfo);
Publish(REQUEST_NAVIGATION_TO_SUBMITION_CONFIRMATION, rosterInfo);
}
ObservableCollection<RequiredField> GetUnsatisfied()
{
RequiredFields.Clear();
RequiredFields = Review();
return RequiredFields;
}
}
Multiple issues:
Have a read at the fundamentals on ReactiveObject, in particular how "Read-Write Properties" are written.
In your case, this.WhenAnyValue(x => x.CanSubmit) will trigger a refresh on the command whenever the property CanSubmit changes, but this one never does, because you never call the setter (and the getter has an incorrect impl).
Currently, your method GetUnsatisfied() has "polling" semantics, which mean you need something to trigger this method to update your command. This isn't reactive at all, you should instead bind/listen to updates.
If there's no way for you to make your Review() logic reactive, then you may do something like:
var canExec = Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1))
.Select(_ => !GetUnsatisfied().Any());
Submit = ReactiveCommand.Create(canExec);
Submit.Subscribe(x => OnSubmit());
Basically, having a timer to do your polling.
(But I strongly suggest going further down the reactive way)
While questions of this sort have been frequently asked, I think I have a more specific constraint that makes the problem a little more interesting. I am writing a client-side application in Dart using an MVC pattern. My goal is simple: listen for clicks on a button, trigger an async request to a back-end API, and present that data to the user.
Minimally, I have one each of a model, view, and controller class. The model class implements methods to make requests and bundle up the data it receives. The view class has the DOM subtree of interest as a field and implements methods to manipulate the elements therein. The controller has a single instance each of the model and view classes as its fields and registers event handlers on the elements of the view. The controller's event handlers fire off calls to the model to make requests and return data, which will then be passed to the view for rendering.
The issue arises when I attempt to capture the incoming data from the async request into an instance variable of the model. I'd like to keep everything nicely encapsulated (that's why I'm using Dart in the first place), and I'd like to avoid using a global variable to hold the data that comes from the async request. A minimal example of my current layout looks something like below. I've made all of the fields and methods public here for clarity's sake.
// view.dart
class FooView {
// The root element of the view with which we're concerned.
static final Element root = querySelector('#thisView');
FooView() { init(); }
void init() { root.hidden = false; }
// Appends the new data into an unordered list.
void update(List<Map<String,String>> list) {
UListElement container = root.querySelector('ul#dataContainer');
container
..hidden = true
..children.clear();
for ( Map<String,String> item in list ) {
container.append(new LIElement()
..id = item['id']
..text = item['text']
);
container.hidden = false;
}
// model.dart
class FooModel {
// Instance variable to hold processed data from the async request.
List<Map<String,String>> dataList;
// Makes async request, returning data to caller.
List<Map<String,String>> getData() {
HttpRequest
.getString('example.com/api/endpoint')
.then( (String data) {
dataList = JSON.decode(data);
});
return dataList;
}
}
// controller.dart
class FooController {
FooModel model;
FooView view;
FooController() {
model = new FooModel;
view = new FooView;
}
void registerHandlers() {
// When this button is clicked, the view is updated with data from the model.
ButtonElement myButton = view.root.querySelector('#myButton');
myButton.onClick.listen( (Event e) {
view.update(model.getData());
});
}
}
The errors I'm seeing involve the model.dataList field coming up null at the end of all of this. My first blush is that I do not understand scoping of callback functions. The way I first understood it, the callback would handle the request's data when it arrived and just set the instance variable when it was ready. Perhaps the instance variable is aliased and modified within the scope of the callback, but the variable I want to return is never touched.
I have thought about passing a Future object to a method of the view, which will then just do the processing itself and add the elements to the DOM as a side effect. That technique would break my MVC design (even more than it's broken now in this minimal working example).
It is also very possible that I am using asynchronous programming completely incorrectly. Thinking more on this, my async call is useless because I basically make a blocking call to view.update() in the controller when the event fires. Maybe I should pass a request Future to the controller, and fire the request's then() method from there when the event handler is triggered.
In Dart, in what scope do callback functions reside, and how can I get data out of them with minimal side effects and maximal encapsulation?
N.B. I hate to belabor this oft-discussed question, but I have read previous answers to similar questions to no avail.
The getData method initiates the asynchronous HTTP request then immediately returns before having received/parsed the response. That is why model.datalist is null.
To make this work with minimal effort, you can make getData synchronous:
(note: I changed the dataList type, just to make it work with the sample JSON service http://ip.jsontest.com/)
// model.dart
class FooModel {
// Instance variable to hold processed data from the async request.
Map<String, String> dataList;
// Makes async request, returning data to caller.
Map<String, String> getData() {
var request = new HttpRequest()
..open('GET', 'http://ip.jsontest.com/', async: false)
..send();
dataList = JSON.decode(request.responseText);
return dataList;
}
}
Though this may violate your objective, I agree with your concerns re: blocking call and would personally consider keeping the HTTP request asynchronous and making getData return a new future that references your model class or parsed data. Something like:
// model.dart
class FooModel {
// Instance variable to hold processed data from the async request.
Map<String,String> dataList;
// Makes async request, returning data to caller.
Future<Map<String, String>> getData() {
return HttpRequest
.getString('http://ip.jsontest.com/')
.then( (String data) {
dataList = JSON.decode(data);
return dataList;
});
}
}
and in the controller:
void registerHandlers() {
// When this button is clicked, the view is updated with data from the model.
ButtonElement myButton = FooView.root.querySelector('#myButton');
myButton.onClick.listen( (Event e) {
model.getData().then((Map<String, String> dataList) {
view.update(dataList);
});
});
}
You return datalist in getData before the HttpRequest has returned.
// Makes async request, returning data to caller.
List<Map<String,String>> getData() {
return HttpRequest // <== modified
.getString('example.com/api/endpoint')
.then( (String data) {
return JSON.decode(data); // <== modified
});
// return dataList; // <== modified
void registerHandlers() {
// When this button is clicked, the view is updated with data from the model.
ButtonElement myButton = view.root.querySelector('#myButton');
myButton.onClick.listen( (Event e) {
model.getData().then((data) => view.update(data)); // <== modified
});
}
You can use Stream to make your design loosely coupled and asynchronous:
class ModelChange {...}
class ViewChange {...}
abstract class Bindable<EventType> {
Stream<EventType> get updateNotification;
Stream<EventType> controllerEvents;
}
class Model implements Bindable<ModelChange> {
Stream<ModelChange> controllerEvents;
Stream<ModelChange> get updateNotification => ...
}
class View implements Bindable<ViewChange> {
Stream<ViewChange> controllerEvents;
Stream<ViewChange> get updateNotification => ...
}
class Controller {
final StreamController<ViewChange> viewChange = new StreamController();
final StreamController<ModelChange> modelChange = new StreamController();
Controller.bind(Bindable model, Bindable view) {
view.controllerEvents = viewChange.stream;
model.controllerEvents = modelChange.stream;
view.updateNotification.forEach((ViewChange vs) {
modelChange.add(onViewChange(vs));
});
model.updateNotification.forEach((ModelChange mc) {
viewChange.add(onModelChange(mc));
});
}
ModelChange onViewChange(ViewChange vc) => ...
ViewChange onModelChange(ModelChange mc) => ...
}