Sometimes Rhino.Mocks is driving me mad, 'cause there's not enough documentation on topics that, I suppose, are relatively easy.
What I want to do is to expect call to AddContact("test", contact). So for the second parameter I must use parameter constraint Property.AllPropertiesMatch(contact). But what should I use for the first one?
_contactManagerMock
.Expect(m => m.AddContact(null, null))
.Constraints(??????????, Property.AllPropertiesMatch(contact));
What goes instead of "??????????"
I was looking for this as well, here is a more detailed answer.
This is an example of how to use the AllPropertyMatch in Rhino.Mocks. I tested this in Rhino.Mocks 3.6.
//arrange
var contactManagerMock = MockRepository.GenerateMock<IManager>();
contactManagerMock.Expect(m => m.AddContact(
Arg.Is("test"),
Arg<Contact>.Matches(Property.AllPropertiesMatch(contact))))
//Act
//Perform action here that should result in the above expected call
//Assert
contactManagerMock.VerifyAllExpectations();
This says to expect the AddContact method to be called. The first parameter should be a string with the value 'test' the second should be an object of type Contact that has all the same properties as the instance of contact.
Calling VerifyAllExpectations performs the assertion.
More info on the Rhino.Mocks site.
Related
I have the following controller in ASP.NET WebApi 2:
[RoutePrefix("Validations")]
public partial class ValidationsController
{
[HttpPost, Route("Bsb")]
public IHttpActionResult ValidateBsb(string value)
{
var validator = new BankStateBranchValidator(DbContext.BankStateBranches);
var data = new ValidationsResult
{
IsValid = validator.IsValid(value ?? string.Empty)
};
data.Error = data.IsValid
? null
: "The BSB you have entered does not appear to be valid. Please check the value and try again.";
return Ok(data);
}
}
For historical reasons, the value parameter needs to be in the querystring, rather than the form body, which should be empty. So the expected API call would be POST /Validate/Bsb?value=012345.
That all works fine, and I get the expected result; however, sometimes we are getting clients calling the API with POST /Validate/Bsb or POST /Validate/Bsb?value=, and that is resulting in a 400 Bad Request response from WebAPI itself, because, as far as I can tell, the model binder is failing to bind the missing value to the parameter. If I put a breakpoint inside the method, it never gets hit.
So, given that I can't change the API contract, how can I handle this scenario? I've tried adding a [ValueProvider(typeof(RouteDataValueProviderFactory))] attribute to the parameter, and my test case for the missing value works, but then the valid value test cases break since the value isn't in the route but in the querystring.
Update
Based on Craig H's suggestion, I've added a default value to the value parameter. So the various scenarios are:
POST /Validate/Bsb?value=012345 - pass (valid value)
POST /Validate/Bsb?value=000000 - pass (invalid value)
POST /Validate/Bsb?value= - fail (empty value)
POST /Validate/Bsb - pass (missing value)
You should be able to make the parameter optional by specifying a default value in the method signature.
e.g.
[HttpPost, Route("Bsb")]
public IHttpActionResult ValidateBsb(string value = null)
Your question says that a query with ?value= was throwing a bad request.
When I tried this locally my breakpoint was hit and value was null.
If I omitted the QS parameter completely, then I received a method not allowed response.
This page makes mention of optional route parameters with attribute routing, although you are not specifying the parameter like that here.
I cannot find the document which describes the other options with regards to routing and optional parameters. I have seen one which indicates the differences between defining it as optional in the route definition, and optional in the method signature. If I find it, I will update this answer!
I have a controller Action that returns a list of activities to a view. (eventID,Venue,Room,EventDescription,EventType,StartDateTime,EndDateTime). The users wanted to be able to filter by Venue so I added Venue as id to the action method
ActionResult ListEvents(id string)
{
... Get the relevant details and return the view with the model
}
Now they want to also be able to filter by any/all of Event Type, Start, End, whether Post-event data has been completed.
Am I better to add these as GET query parameters or to define a custom route that will accept all 5 arguments or is there another solution
I will also need to add sorting and pagination at some point in case this changes the suggestion.
Typically, these would be handled via a query string, but it doesn't matter how you do it really. Regardless, of how the parameters are sent, your action simply needs to accept them all. The only thing you have to be aware of is the standard C# method rule (since actions are just methods) that optional parameters must be the last ones on the method. If they're all optional, then even that isn't really a concern.
Basically, you just have something like:
public ActionResult ListEvents(string id = null, int? eventID = null, ...)
{
Then inside, you'd just do something like:
var events = db.Events;
if (eventID.HasValue)
{
events = events.Where(m => m.EventID == eventId);
}
// rinse and repeat for each filter
$new_contact = PodioContact::create(
2144836,
new PodioContact(
array('name' => $name,'title'=>$title, 'organization'=>$org, 'phone' => $phone, 'mail' => $email)
)
);
Above is the method of creating a new contact. It accepts 2 arguments, first is a integer for workspace id, the second one is an Contact object which holds the contact details.
Referring to here https://developers.podio.com/doc/contacts/create-space-contact-65590, I clearly know what should be the first argument, which was the workspace id.
However the second argument is stated as $attributes = array() in the API Doc which is an array. I ASSUMED this to be a key value array of the contact's properties. I proceeded to pass a key value array into the second argument like so:
$new_contact = PodioContact::create(
2144836,
array('name' => $name,'title'=>$title, 'organization'=>$org, 'phone' => $phone, 'mail' => $email)
);
It kept failing to work. After struggling and wasting 1 hour. I simply tried to pass an Contact Object as the second argument as shown in the beginning of this post. So with this trial and error and wasting a large amount of time, I discovered what should be the second argument by luck.
So my question is, why is the API Doc showing the second argument should be an array? Is the documentation wrong or am I missing something? Could you please tell me if I did anything wrong here so I don't have to trial and error and waste 1~2hours to figure out the second argument.
It's really much the same thing. When you pass in a Podio* object as the attributes parameter podio-php will serialize it by calling the as_json method on that object (and it'll then be an associative array). If you kick podio-php into a debug mode you can see exactly what's being sent over the wire: http://podio.github.io/podio-php/debug/
I've just started using this and immediately ran into a problem mapping a parameter to a a constructor parameter.
I've tried every example I can find on SO but none seem to work and the documentation doesn't mention this feature as far as I can see.
The examples show:
Mapper.CreateMap<UserProfile, UserProfileModel>().ConstructUsing(x => new UserProfileModel(x.Id);
I can't figure out the syntax to get access to the Id property on the UserProfile object.
Another example shows:
Mapper.CreateMap<TypeOneDto, TypeOne>().ConstructUsing((Func<ResolutionContext, TypeOne>) (r => new TypeOne()));
On using any of these lambdas I just have access to the ResolutionContext not the parent object?
Any Ideas?
Is it possible to do an overload of the actions in the controller? I haven't found any info about it and when I tried, I got this error:
The current request for action 'Create' on controller type 'InterviewController' is >ambiguous between the following action methods:
System.Web.Mvc.ViewResult Create() on type >MvcApplication4.MvcApplication4.InterviewController
System.Web.Mvc.ViewResult Create(Int32) on type >MvcApplication4.MvcApplication4.InterviewController
I've tried to do this on another way and I also get a new error that I can't fix. In fact, I created a new action (called create_client instead of create)
I need 2 ways of creating an "opportunite".
I just call the action, and I receive an empty formular in which I just have to insert data.
From a client's page, I must create an "opportunite" with the client that's already completed when the form is displayed to the user. (there is a need of productivity, the user must perform actions as fast as possible).
In the table "opportunite", I've got a column called "FK_opp_client", which is equal to the column "idClient" from the client's table.
I don't get how I can do the second way.
I've created a new action in the controller.
'
' GET: /Opportunite/Create_client
Function Create_client(idclient) As ViewResult
'Dim FK_Client = (From e In db.client
'Where(e.idClient = idclient)
' Select e.nomCompteClient).ToString()
'ViewBag.FK_client = New SelectList(db.client, "idClient", "nomCompteClient", idclient)
Dim opportunite As opportunite = db.opportunite.Single(Function(o) o.idOpportunite = 5)
opportunite.FK_Client = idclient
ViewBag.FK_Client = New SelectList(db.client, "idClient", "nomCompteClient", opportunite.FK_Client)
Return View(opportunite)
End Function
I've tried a few things to get what I wanted, the last one was to copy what was done in the "Edit" action, but for an empty rank. (so I created an empty rank in my DB). I don't think it was a good idea (imagine someone wants to update the DB where idOpportunite = 5...)
Any better ideas?
If you want to keep those two methods under the same name, you will have to implement an ActionSelectionAttribute to decorate them, or use them with different verbs (for example POST and PUT). Please read more details on action method selection process here (old but still true).
Different approach might be making your parameter optional and make action to check if it has been passed or not (through nullable type).