Rowfixture in .NET to call a web service with Inputs and validate response object - fitnesse

I have the following FitNesse test implemented against .NET. It uses "RowFixture" to return an object which is verified. All this works ok.
My Question is, how can I pass the "inputs" to the array from the FIT test?
At the monent, this is hard coded internally.
Here is the FIT test:
!|ReturnObjectMultiDimension|
|Id |Name |
|1, 2, 3, 4 |a, b, c, d |
Here is the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using fit;
using dbfit;
namespace DbFitProject
{
public class ReturnObjectMultiDimension : RowFixture
{
public override Type GetTargetClass()
{
return typeof(CustomerObject);
}
public override object[] Query()
{
CustomerObject[] array = new CustomerObject[1];
array[0] = new CustomerObject(new int[4] { 1, 2, 3, 4 }, new string[4] {"a","b","c","d" });
return array;
}
}
public class CustomerObject
{
public int[] Id;
public string[] Name;
public CustomerObject(int[] Id, string[] Name)
{
this.Id = Id;
this.Name = Name;
}
}
}
Thanks for help.

It's simpler to just have a class that creates a customer object list and fitSharp will automatically wrap the list in a fixture to check the results:
public class MyClass {
public List<CustomerObject> MakeListWithIdsNames(int[] ids, string[] names) {
return new List<CustomerObject> { new CustomerObject(ids, names) };
}
}
|my class|
|make list with ids|1,2,3,4|names|a,b,c,d|
|id|name|
|1,2,3,4|a,b,c,d|
See http://fitsharp.github.io/Fit/FixtureWrapper.html

Related

Using a controller with WEB API Routing

In my project (Asp.net MVC), I want to use DevExtreme GridView to display my data. I've used code first to create databases and tables. In the project, I have a model with the name of Member. I did right click on the Controller folder and select Add->Controller->DevExtreme Web API Controller with actions, using Entity Framework. In the wizard, I selected my database context and model and determine my controller name (MembersController) and then clicked Add. So in the Views folder, I created a folder with name Members and inside it, I added a view with name Index. (I don't know what exactly name must be for view, you suppose Index). In the index view, I used the wizard to add a DevExtreme GridView (Right-click on the view context and click on Insert A DevExtreme Control Here. In the wizard, I selected GridView as control and DatabaseContext, Member model and Members controller. You can see all of my codes in the below:
Member Mode:
Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace WebApplication2.Models
{
public class Member
{
#region Ctor
public Member()
{
}
#endregion
#region Properties
[Key]
public int MemberID { get; set; }
[Required(ErrorMessage ="*")]
public string FirstName { get; set; }
[Required(ErrorMessage = "*")]
public string LastName { get; set; }
public string Phone { get; set; }
public string Mobile { get; set; }
[Required(ErrorMessage = "*")]
public string NID { get; set; }
[Required(ErrorMessage = "*")]
public string MID { get; set; }
[Required(ErrorMessage = "*")]
public string SalaryID { get; set; }
#endregion
}
}
Controller:
[Route("api/Members/{action}", Name = "MembersApi")]
public class MembersController : ApiController
{
private ApplicationDbContext _context = new ApplicationDbContext();
[HttpGet]
public HttpResponseMessage Get(DataSourceLoadOptions loadOptions) {
var members = _context.Members.Select(i => new {
i.MemberID,
i.FirstName,
i.LastName,
i.Phone,
i.Mobile,
i.NID,
i.MID,
i.SalaryID
});
return Request.CreateResponse(DataSourceLoader.Load(members, loadOptions));
}
[HttpPost]
public HttpResponseMessage Post(FormDataCollection form) {
var model = new Member();
var values = JsonConvert.DeserializeObject<IDictionary>(form.Get("values"));
PopulateModel(model, values);
Validate(model);
if (!ModelState.IsValid)
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, GetFullErrorMessage(ModelState));
var result = _context.Members.Add(model);
_context.SaveChanges();
return Request.CreateResponse(HttpStatusCode.Created, result.MemberID);
}
[HttpPut]
public HttpResponseMessage Put(FormDataCollection form) {
var key = Convert.ToInt32(form.Get("key"));
var model = _context.Members.FirstOrDefault(item => item.MemberID == key);
if(model == null)
return Request.CreateResponse(HttpStatusCode.Conflict, "Member not found");
var values = JsonConvert.DeserializeObject<IDictionary>(form.Get("values"));
PopulateModel(model, values);
Validate(model);
if (!ModelState.IsValid)
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, GetFullErrorMessage(ModelState));
_context.SaveChanges();
return Request.CreateResponse(HttpStatusCode.OK);
}
[HttpDelete]
public void Delete(FormDataCollection form) {
var key = Convert.ToInt32(form.Get("key"));
var model = _context.Members.FirstOrDefault(item => item.MemberID == key);
_context.Members.Remove(model);
_context.SaveChanges();
}
private void PopulateModel(Member model, IDictionary values) {
string MEMBER_ID = nameof(Member.MemberID);
string FIRST_NAME = nameof(Member.FirstName);
string LAST_NAME = nameof(Member.LastName);
string PHONE = nameof(Member.Phone);
string MOBILE = nameof(Member.Mobile);
string NID = nameof(Member.NID);
string MID = nameof(Member.MID);
string SALARY_ID = nameof(Member.SalaryID);
if(values.Contains(MEMBER_ID)) {
model.MemberID = Convert.ToInt32(values[MEMBER_ID]);
}
if(values.Contains(FIRST_NAME)) {
model.FirstName = Convert.ToString(values[FIRST_NAME]);
}
if(values.Contains(LAST_NAME)) {
model.LastName = Convert.ToString(values[LAST_NAME]);
}
if(values.Contains(PHONE)) {
model.Phone = Convert.ToString(values[PHONE]);
}
if(values.Contains(MOBILE)) {
model.Mobile = Convert.ToString(values[MOBILE]);
}
if(values.Contains(NID)) {
model.NID = Convert.ToString(values[NID]);
}
if(values.Contains(MID)) {
model.MID = Convert.ToString(values[MID]);
}
if(values.Contains(SALARY_ID)) {
model.SalaryID = Convert.ToString(values[SALARY_ID]);
}
}
private string GetFullErrorMessage(ModelStateDictionary modelState) {
var messages = new List<string>();
foreach(var entry in modelState) {
foreach(var error in entry.Value.Errors)
messages.Add(error.ErrorMessage);
}
return String.Join(" ", messages);
}
protected override void Dispose(bool disposing) {
if (disposing) {
_context.Dispose();
}
base.Dispose(disposing);
}
}
View:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
#(Html.DevExtreme().DataGrid<WebApplication2.Models.Member>()
.DataSource(ds => ds.WebApi()
.RouteName("MembersApi")
.LoadAction("Get")
.InsertAction("Post")
.UpdateAction("Put")
.DeleteAction("Delete")
.Key("MemberID")
)
.RemoteOperations(true)
.Columns(columns => {
columns.AddFor(m => m.MemberID);
columns.AddFor(m => m.FirstName);
columns.AddFor(m => m.LastName);
columns.AddFor(m => m.Phone);
columns.AddFor(m => m.Mobile);
columns.AddFor(m => m.NID);
columns.AddFor(m => m.MID);
columns.AddFor(m => m.SalaryID);
})
.Editing(e => e
.AllowAdding(true)
.AllowUpdating(true)
.AllowDeleting(true)
)
)
WebApiConfig.cs file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace WebApplication2
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// WebAPI when dealing with JSON & JavaScript!
// Setup json serialization to serialize classes to camel (std. Json format)
var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
formatter.SerializerSettings.ContractResolver =
new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
}
}
}
Global.asax.cs file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace WebApplication2
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
In addition I've installed all requirements for this project according this link.
But when I try to show View with https://localhost:44328/Members/index RUL, I get this error:
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /Members/index
I'v tried a lot way to correct my wrong but I couldn't find solution. I almost read all of documents about routing (mvc and web api), but after about 5 days I still couldn't to solve it.
Thanks a lot for answer me.
The thing is as far as I can tell, one of the reasons you are receiving a 404 is because you don't seem to be adding your parameter anywhere. Aside from that your 'DataSourceLoadOptions loadOptions' shouldn't be used as a parameter because it is probably too complex. Shouldn't you create a service which retrieves your loadOptions instead of you giving it along?
If you want all members without giving information then you should do exactly that. Not give the request some metadata it doesn't know about along for the ride.
I suggest you do the following:
Create an API which does not need metadata like how to get a datasource. Things such as Members.LastName are acceptable
Make sure you create a service which is responsible for getting your data in the first place. This means also removing all that extra code in your controller and placing it in a more suitable location.
Keep your classes clean and simple. Your controller now has too many responsibilities.
Hopefully this'll help. If you try your API GET Method as is without the 'DataSourceLoadOptions loadOptions' parameter, then your API will not return 404.
Since you didn't put in your ajax call url, I'm going to have to work with this
Requested URL: /Members/index
This is a problem, your webApi default route requires your URL to be prepended with /api/
So something like this should work /api/Members, so you can remove the Index part of that URL as the request type will handle which Action is executed ie HTTPGet/HTTPPost
EDIT: Use this as your route
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { controller = "Members" id = RouteParameter.Optional }
);

Is there get method which not found while run web api service

I'm creating a web api controller and implement ([HttpGet])parameterized get method with datatype class but when I run this its show 404 Not Found.
When i am implementing normal datatype like string or int its show me the answer or datatype like List still it's giving me answer but when I directly declare datatype as class like Student its show 404 not found error. I don't know why it's so. I am trying to learn web api with mvc please help me.
I am creating one simple class Student and one studentapicontroller.In my api controller, I create get method with datatype class and for testing purpose i make other get method with different datatype
Student.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace epay.Models
{
public class student
{
public int id { get; set; }
public string name { get; set; }
}
}
studentapiController :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using epay.Models;
namespace epay.Controllers
{
public class studentapiController : ApiController
{
// GET api/studentapi
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/studentapi/5
{
return "value";
}
// POST api/studentapi
public void Post([FromBody]string value)
{
}
// PUT api/studentapi/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/studentapi/5
public void Delete(int id)
{
}
//POST api/studentapi/
[HttpGet]
public student getdetails(int id, string na)
{
student st = new student();
st.id = id;
st.name = na;
return st;
}
//GET api/studentapi/getstud
[HttpGet]
public List<student> getstud()
{
List<student> lst = new List<student>();
student st = new student();
st.name = "manlai";
st.id = 5;
lst.Add(st);
return lst;
}
}
}
I just want getdetails result or how to do if I want my method datatype as a class and I am passing parameter with my get method how to do this
Make getdetails route like below because it is conflicting with get
[HttpGet("getdetails")]
public student getdetails(int id, string na)
{
student st = new student();
st.id = id;
st.name = na;
return st;
}
you can call route something like this with querystring /studentapi/getdetails?id=1&na=test

Web API - Entity Framework is trying to ruin my life

I have a simple database with five tables bound together by relationships. I've built OData apps before and like the model, but wanted to make the leap to Web API. The problem is - when I make my call to the ProductType table for example - it returns EVERY record in every table! What gives?
My goal is to call a ProductType for example like this:
url/api/ProductType
(and the usual getbyId, delete, etc)
I'm using the repository pattern, and following these steps:
1. Create a Entity Framework model with the tables I want to display. Yawn.
2. Add an interface in Models, call it IProductTypeRepository, with the behaviors I want to see.
3. Add a model class (ProductModel.cs) that inherits from this interface
4. Make sure Global.asax has either default api setup or there’s a App_Start\Routeconfig and WebApiConfig class.
5. Surgery on the ValuesController class.
Maybe this is in ignorance of how WebAPI is supposed to work with relationships. Perhaps the goal is to do one call to a service - and get EVERY record across multiple tables (for example, Product and ProductDetails). If so, I think the documentation could have been a LOT clearer on this point!
============================CODE FOLLOWS!================================================
I’m going to skip the EF auto-generated code obviously – but suffice to say it contains entities and joins for five tables – ProductType, Location, Process, Step, and StepType. ProductType is a very simple table with an ID field and a description and a part # field (MaterialMasterId).
For IProductTypeRepository, here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Perceptive.Services.Models
{
interface IProductTypeRepository
{
IEnumerable<ProductType> GetAll();
ProductType GetById(int ProductTypeID);
int Update(ProductType producttype);
ProductType Add(ProductType producttype);
void Delete(int ProductTypeId);
}
}
For ProducTTypeModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Perceptive.Services.Models
{
public class ProductTypeModel : IProductTypeRepository
{
private PCSEntities1 context = new PCSEntities1();
public IEnumerable<ProductType> GetAll()
{
return context.ProductTypes;
}
public ProductType GetById(int ProductTypeId)
{
IQueryable<ProductType> producttypes = context.ProductTypes.Where(a => a.ProductTypeID == ProductTypeId);
return producttypes.FirstOrDefault();
}
public int Update(ProductType producttype)
{
ProductType updateProductType = context.ProductTypes.FirstOrDefault(c => c.ProductTypeID == producttype.ProductTypeID);
updateProductType.Description = producttype.Description.Trim();
updateProductType.MaterialMasterID = producttype.MaterialMasterID;
return context.SaveChanges();
}
public ProductType Add(ProductType producttype)
{
var addedProductType = context.ProductTypes.Add(producttype);
context.SaveChanges();
return addedProductType;
}
public void Delete(int producttypeid)
{
ProductType producttype = context.ProductTypes.FirstOrDefault(a => a.ProductTypeID == producttypeid);
context.ProductTypes.Remove(producttype);
context.SaveChanges();
}
}
}
… and lastly, my Controller – ProductTypeController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
//dh added
using Perceptive.Services.Models;
namespace Perceptive.Services.Controllers
{
public class ProductTypeController : ApiController
{
IProductTypeRepository repository = new ProductTypeModel();
// GET /api/Producttype
[HttpGet]
public IEnumerable<ProductType> Get()
{
return repository.GetAll();
}
// GET /api/Producttype/5
[HttpGet]
public ProductType Get(int id)
{
ProductType producttype = repository.GetById(id);
return producttype;
}
// POST /api/ProducTtype
[HttpPost]
public void PostProductType(ProductType producttype)
{
repository.Add(producttype);
}
// PUT /api/ProductType
// DH seems scanty here. Where's the repository put?
[HttpPut]
public void PutProductType(ProductType producttype)
{
if (repository.Update(producttype) == 0)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
//DELETE /api/ProductType/5
[HttpDelete]
public void Delete (int id)
{
repository.Delete(id);
}
}
}
You need to use IQueryable:
interface IProductTypeRepository
{
IQueryable<ProductType> GetAll();
ProductType GetById(int ProductTypeID);
int Update(ProductType producttype);
ProductType Add(ProductType producttype);
void Delete(int ProductTypeId);
}
When you use IEnumerable it is going to pull all the records in the entity client side and then work on them. IQueryable is treated as a query until you call an action like .ToList() which will execute the query and pull the results to the client.

JSON Deserialization for polymorphic array in MVC 4 controller

Im using MVC 4 my ActionController recives the following Json:
{
"MainId": 1,
"Actions": [
{
"Attribute1ClassA": 1,
"Attribute2ClassA": 2
},
{
"Attribute1ClassB": 3,
"Attribute2ClassB": 4
},
{
"Attribute1ClassC": 5
}
]
}
and the Controller:
[HttpPost]
public ActionResult Commit(ActionsSummaryViewModel summary)
{
//DO stuff
}
and declaration for classes:
public ActionsSummaryViewModel
{
public int MainId {get;set;}
public IList<MainClass> {get;set;}
}
public class MainClass
{
}
public class ClassA : MainClass
{
public int Attribute1ClassA {get;set;}
public string Attribute2ClassA {get;set;}
}
public class ClassB : MainClass
{
public int Attribute1ClassB {get;set;}
public string Attribute2ClassB {get;set;}
}
public class ClassC : MainClass
{
public int Attribute1ClassC {get;set;}
}
So now, how can i manage the deserialization for the MainClass when the action controller receive the JSON ? because when i call the action the list items are null.
if part of the solution is Json.NET, how i can implement for MVC 4 controllers?
Thanks for your help.
You need a property or set of properties from which you can determine which type the class is to use this method. Using JSON.NET, I deserialize the incoming JSON as a dynamic object, then check the common property, determine the type, and deserialize the value again this type using my model type:
// I'm assuming here you've already got your raw JSON stored in 'value'
// In my implementation I'm using the Web API so I use a media formatter,
// but the same principle could be applied to a model binder or however
// else you want to read the value.
dynamic result = JsonConvert.DeserializeObject(value);
switch ((string)result.type)
{
case "typeone":
return JsonConvert.DeserializeObject<ModelOne>(value);
// ...
default: return null;
}
There's a little bit of extra overhead here because you're deserializing twice, but it's worth it in most cases to me because it's easy to understand what's going on and add new types as needed.
You could parse JSON into dynamic object instead using Json.NET:
using Newtonsoft.Json.Linq:
dynamic data = JObject.Parse("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");
string name = data.Name;
string address = data.Address.City;

Best way to filter domain objects for json output in an ASP.NET MVC application

If I'm rendering a regular view in asp.net mvc the only domain object properties that show up in my page the ones I specifically write out. For example:
<div><%= Customer.FirstName %></div>
However, if I serialize a domain object for json it will include every property. Example:
public JsonResult Customer (int? id)
{
Customer customer = _serviceLayer.GetCustomer (id.Value);
return Json (customer);
}
Since I don't want every Customer property exposed what is the best way to filter the output properties for json in this case? Can you use an include/exclude list like UpdateModel()? Use a proxy class such as public class JsonCustomer? What would you recommend?
I use anonymous types for this:
var customer = from c in serviceLayer.GetCustomers()
where c.Id == id.Value
select new { FirstName = c.FirstName };
This is not just a good idea. Rather, it's protection against the exception that you will get when calling Json() if your object graph contains a circular reference.
You may use the [ScriptIgnore] attribute (in System.Web.Extensions). See http://www.creave.dk/post/2009/10/07/Excluding-properties-from-being-serialized-in-ASPNET-MVC-JsonResult.aspx for an example.
Please use a view model. A view model is an object that the UI uses to represent your domain objects on the screen. Each screen has its own view model.
When you make your view model, which is a DTO, which is a flattened, null-safe projection of domain objects, do not map properties you do not wish to be displayed on the screen.
Serialize the view model, not your domain object.
You could use the Newtonsoft library and [JsonIgnore] attribute for marking properties of your class you don't want to expose. There are other libraries (with possible different property ignore attribute name), I personally prefer this one since it's very flexible in JSON converter extensions etc + it can easily serialize anonymous objects.
public class Customer
{
...
[JsonIgnore]
public string UrlIn { get; set; }
public string FirstName { get; set; }
// following example of a converter, you could write your own as well
[JsonConverter(typeof(Newtonsoft.Json.Converters.JavaScriptDateTimeConverter))]
public DateTime Created { get { return _created; } }
}
In my experience, the JavaScriptSerializer respects some of the XmlSerialization attributes such as XmlIgnore too.
I ran into the same problem and THE ONE OF SOLUTION IS to
Use [ScriptIgnore] atribute .. it will solve the problem.
add system.web.extensions reference and add namespace:
Using System.Web.Script.Serialization.
If you still have questions..Read on..for some detailed explanation..
i have a User class with..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Iesi.Collections.Generic;
using System.Runtime.Serialization;
namespace RAPortal.Core.Entities
{
public class User {
private int _userId;
private string _firstName;
private string _lastName;
private IList < Application > _applications;
private IList < Group > _groups;
private IList < ApplicationRequest > _applicationRequests;
....Properties..
public virtual int UserId {
get { return _userId; }
set { _userId = value; }
}
public virtual string Title {
get { return _title; }
set {
_title = !String.IsNullOrEmpty(value) ? value.ToUpper().Trim() : null;
}
}
public virtual IList < Group > Groups {
get { return _groups; }
set { _groups = value; }
}
public virtual IList < UserPrivilege > UserPrivileges
{
get { return _userPrivileges; }
set { _userPrivileges = value; }
}
public virtual IList < UserRole > UserRoles {
get { return _userRoles; }
set { _userRoles = value;}
}
...so on...
and I have Groups class..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Script.Serialization;
using System.Runtime.Serialization;
namespace RAPortal.Core.Entities {
public class Group {
private int _groupId;
private string _name;
private IList < User > _users;
public virtual int GroupId {
get { return _groupId; }
set { _groupId = value; }
}
public virtual string Name {
get { return _name; }
set {
_name = !String.IsNullOrEmpty(value) ? value.ToUpper().Trim() : null;
}
}
[ScriptIgnore]
public virtual IList < User > Users {
get { return _users; }
set { _users = value; }
}
}
}
Since User is referenced in the groups.. the json think that it is Circular reference and It will throw an exception..so the fix is to add [ScriptIgnore] on top of the User. And add the reference and namespace to this class like it..
It solved my problem .. I am sure there are better ways out there !!! Cheers...
And remember you should add [scriptIgnore] only in the groups class and not in the Users class..

Resources