In my integration test I do the following:
void testSave() {
def controller = new BookController()
controller.params.title = "Awesome Book"
controller.params.pages = 1000
controller.save()
// i want to check book data here
}
How can I retrieve the Book my controller persisted and check that the title == "Awesome Book" and that pages == 1000? Can I somehow access the bookInstance variable in my controller from the test?
I cannot assume that Book.get(1) will give me the correct Book because there will be bootstrap data in the database. The controller redirects to a completely Book unrelated page so I cannot get the id from the URL. The only thing I can think of is to do something like this:
void testSave() {
def oldIdList = Book.list()*.id
// set up the parameters and call controller.save()
def insertedId = Book.list()*.id - oldIdList
// check Book.get(insertedId) properties
}
but I'm hoping there is a better way...
Assuming you are using Grails 2.0.x you can look up the book by any of its properties using the findBy* methods:
def book = Book.findByTitle("Awesome Book")
assertNotNull book
Related
Why we return the own page in which method?
For example:
public class Page{
public Page testElement(){
#code
return this;
}
Why do i do that? Can i return other pages, if I, why can i?
yes you can call them after, something like installProcess.installProcessAndConnectTo(). mainScreen.clickOnPhoneIcon.dialer.openAPPSettings();
As covered in the comments + with a bit more detail.
Yes you can return other pages.
Returning a Page class is a design approach to allow method chaining and provide a more guided approach to test creation.
For this approach you want to the return the page you expect to be after your action. For example, after you click Login you're expect to be on the home page - so return that page.
If there is a return it allows you you to create more fluent-like statements and use inteli-sense to aid your creation.
For example if there is a return on the method you can do 1 liners such as:
login.SetUsername("hello").SetPassword("world").ClickLogin().DoSoemthingOnHomePage();
That's compared to the normal long form:
var login = new login(driver);
login.SetUsername("hello");
login.SetPassword("world");
login.ClickLogin();
var home = new Home(driver);
home.DoSoemthingOnHomePage();
What is the recommended design for reusing WebAPI methods from one WebAPI controller in another?
For example, suppose I have a BooksController that allows a book to be found using its title:
[RoutePrefix("Books")]
public class BooksController : ApiController
{
EntityModelContext db = new EntityModelContext();
[Route("{title}")]
[HttpGet]
[ResponseType(typeof(BookDto))]
public IHttpActionResult FindByTitle(string title)
{
var book = this.db.Books
.Select(b => new BookDto
{
Id = b.BookId,
Title = b.Title,
Author = b.Author
})
.SingleOrDefault(b => b.Title == title);
if(book == null)
{
return this.NotFound();
}
return this.Content(HttpStatusMessage.OK, book);
}
}
e.g. You could find this book by accessing /Books/Gone%20with%20the%20Wind.
Now say I have a second controller that retrieves the chapters of the book if a valid book exists, e.g. /Books/Gone%20%with%20the%20Wind/Chapters.
I could just copy and paste the logic from the Books controller method into the BookChapters controller, and then add the additional logic of retrieving the list of chapters (I need to get the BookId in order to retrieve the list of chapters). But this is not very DRY.
What I would like to do is call the BooksController.FindByTitle method from the BookChapters controller method, return if the response is not HttpStatusMessage.OK, or read the content (to get the BookId) and continue with the additional chapter logic if it is. However this seems it would involve a pretty clunky solution since the method I am consuming returns an IHttpActionResult. Is there an easier/cleaner way to do this?
I saw a related question:
Sitecore Glass Mapper always null
But unfortunately it does not give a solution for my case.
Here goes a code snippet:
var db = Factory.GetDatabase("master");
var context = new SitecoreContext();
// the ID of Needed item
var g = new Guid("{F21C04FE-8826-41AB-9F3C-F7BDF5B35C76}");
// just to test if it's possible to fetch item using db.GetItem
var i = db.GetItem(new ID(g), Language.Current, Sitecore.Data.Version.Latest);
// Grab item
var t = context.GetItem<Article>(g);
In the code above:
i is not null
t is null
Article is the simple class like:
[SitecoreType(TemplateId = "{4C4EC1DA-EB77-4001-A7F9-E4C2F61A9BE9}")]
public class Article
{
[SitecoreField(FieldName = "Title")]
public string Title { get; set; }
}
There are only one language installed in Sitecore - en, it has been specified in the web.config in the items as well.
Also I have added GlassMapperSc.Start(); to Application_Start in the Global.asax.cs and added my assembly to the list of included assemblies via var attributes = new AttributeConfigurationLoader(new[] { "Assembly.Name" }); and I succeeded to find my class in the SitecoreContext mappings.
It does not looks like a language issue, as stated in the link provided in the very beginning. And I'm struggling with it already for a pretty long time, but no luck...
Thank You!
I just noticed that you are using master db for the Sitecore DB and SitecoreContext for Glass.
The SitecoreContext class will use the database that is defined by the Sitecore.Context.Database property at runtime. This probably means that it is using the web database.
Can you check that you have published the item to the web database or instead using:
var context = new SitecoreService("master");
I am currently working on an Umbraco MVC 4 project version 6.0.5. The project currently uses Vega.USiteBuilder to build the appropriate document types in the backoffice based on strongly typed classes with mapping attributes. Consequently, all my razor files inherit from UmbracoTemplatePageBase
I am coming across a road block trying to invoke a HTTP GET from a razor file. For example a search form with multiple fields to submit to a controller action method, using a SurfaceController using Html.BeginUmbracoForm.
My Html.BeginUmbracoForm looks like this
#using (Html.BeginUmbracoForm("FindTyres", "TyreSearch"))
{
// Couple of filter fields
}
I basically have a scenario where I will like to retrieve some records from an external database outside of Umbraco (external to Umbraco Database) and return the results in a custom view model back to my Umbraco front end view. Once my controller and action method is setup to inherit from SurfaceController and thereafter compiling it and submitting the search, I get a 404 resource cannot be found where the requested url specified: /umbraco.RenderMVC.
Here is my code snippet:
public ActionResult FindTyres(string maker, string years, string models, string vehicles)
{
var tyreBdl = new Wheels.BDL.TyreBDL();
List<Tyre> tyres = tyreBdl.GetAllTyres();
tyres = tyres.Where(t => string.Equals(t.Maker, maker, StringComparison.OrdinalIgnoreCase)
&& string.Equals(t.Year, years, StringComparison.OrdinalIgnoreCase)
&& string.Equals(t.Model, models, StringComparison.OrdinalIgnoreCase)
&& string.Equals(t.Version, vehicles, StringComparison.OrdinalIgnoreCase)).ToList();
var tyreSearchViewModel = new TyreSearchViewModel
{
Tyres = tyres
};
ViewBag.TyreSearchViewModel = tyreSearchViewModel;
return CurrentUmbracoPage();
}
I then resort to using standard MVC, Html.BeginForm (the only difference). Repeating the steps above and submitting the search, I get the following YSOD error.
Can only use UmbracoPageResult in the context of an Http POST when
using a SurfaceController form
Below is a snippet of the HTML BeginForm
#using (Html.BeginForm("FindTyres", "TyreSearch"))
{
// Couple of filter fields
}
I feel like I am fighting the Umbraco routes to get my controller to return a custom model back to the razor file. I have googled alot trying to figure out how to do a basic search to return a custom model back to my Umbraco front end view till the extent that I tried to create a custom route but that too did not work for me.
Does my controller need to inherit from a special umbraco controller class to return the custom model back? I will basically like to invoke a HTTP GET request (which is a must) so that my criteria search fields are reflected properly in the query strings of the url. For example upon hitting the search button, I must see the example url in my address browser bar
http://[domainname]/selecttyres.aspx/TyresSearch/FindTyresMake=ASIA&Years=1994&Models=ROCSTA&Vehicles=261
Therefore, I cannot use Surface Controller as that will operate in the context of a HTTP Post.
Are there good resource materials that I can read up more on umbraco controllers, routes and pipeline.
I hope this scenario makes sense to you. If you have any questions, please let me know. I will need to understand this concept to continue on from here with my project and I do have a deadline.
There are a lot of questions about this and the best place to look for an authoritative approach is the Umbraco MVC documentation.
However, yes you will find, if you use Html.BeginUmbracoForm(...) you will be forced into a HttpPost action. With this kind of functionality (a search form), I usually build the form manually with a GET method and have it submit a querystring to a specific node URL.
<form action="#Model.Content.Url"> ... </form>
On that page I include an #Html.Action("SearchResults", "TyresSearch") which itself has a model that maps to the keys in the querystring:
[ChildAction]
public ActionResult(TyreSearchModel model){
// Find results
TyreSearchResultModel results = new Wheels.BDL.TyreBDL().GetAllTyres();
// Filter results based on submitted model
...
// Return results
return results;
}
The results view just need to have a model of TyreSearchResultModel (or whatever you choose).
This approach bypasses the need for Umbraco's Controller implementation and very straightforward.
I have managed to find my solution through route hijacking which enabled me to return a custom view model back to my view and work with HTTP GET. It worked well for me.
Digby, your solution looks plausible but I have not attempted at it. If I do have a widget sitting on my page, I will definitely attempt to use your approach.
Here are the details. I basically override the Umbraco default MVC routing by creating a controller that derived from RenderMvcController. In a nutshell, you implement route hijacking by implementing a controller that derives from RenderMvcController and renaming your controllername after your given documenttype name. Recommend the read right out of the Umbraco reference (http://our.umbraco.org/documentation/Reference/Mvc/custom-controllers) This is also a great article (http://www.ben-morris.com/using-umbraco-6-to-create-an-asp-net-mvc-4-web-applicatio)
Here is my snippet of my code:
public class ProductTyreSelectorController : Umbraco.Web.Mvc.RenderMvcController
{
public override ActionResult Index(RenderModel model)
{
var productTyreSelectorViewModel = new ProductTyreSelectorViewModel(model);
var maker = Request.QueryString["Make"];
var years = Request.QueryString["Years"];
var models = Request.QueryString["Models"];
var autoIdStr = Request.QueryString["Vehicles"];
var width = Request.QueryString["Widths"];
var aspectRatio = Request.QueryString["AspectRatio"];
var rims = Request.QueryString["Rims"];
var tyrePlusBdl = new TPWheelBDL.TyrePlusBDL();
List<Tyre> tyres = tyrePlusBdl.GetAllTyres();
if (Request.QueryString.Count == 0)
{
return CurrentTemplate(productTyreSelectorViewModel);
}
if (!string.IsNullOrEmpty(maker) && !string.IsNullOrEmpty(years) && !string.IsNullOrEmpty(models) &&
!string.IsNullOrEmpty(autoIdStr))
{
int autoId;
int.TryParse(autoIdStr, out autoId);
tyres = tyres.Where(t => string.Equals(t.Maker, maker, StringComparison.OrdinalIgnoreCase) &&
string.Equals(t.Year, years, StringComparison.OrdinalIgnoreCase) &&
string.Equals(t.Model, models, StringComparison.OrdinalIgnoreCase) &&
t.AutoID == autoId)
.ToList();
productTyreSelectorViewModel.Tyres = tyres;
}
else if (!string.IsNullOrEmpty(width) && !string.IsNullOrEmpty(aspectRatio) && !string.IsNullOrEmpty(rims))
{
tyres = tyres.Where(t => string.Equals(t.Aspect, aspectRatio, StringComparison.OrdinalIgnoreCase) &&
string.Equals(t.Rim, rims, StringComparison.OrdinalIgnoreCase)).ToList();
productTyreSelectorViewModel.Tyres = tyres;
}
var template = ControllerContext.RouteData.Values["action"].ToString();
//return an empty content result if the template doesn't physically
//exist on the file system
if (!EnsurePhsyicalViewExists(template))
{
return Content("Could not find physical view template.");
}
return CurrentTemplate(productTyreSelectorViewModel);
}
}
Note my ProductTyreSelectorViewModel must inherit from RenderModel for this to work and my document type is called ProductTyreSelector. This way when my model is returned with the action result CurrentTemplate, the Umbraco context of the page is retained and my page is rendered appropriately again. This way, all my query strings will show all my search/filter fields which is what I want.
Here is my snippet of the ProductTyreSelectorViewModel class:
public class ProductTyreSelectorViewModel : RenderModel
{
public ProductTyreSelectorViewModel(RenderModel model)
: base(model.Content, model.CurrentCulture)
{
Tyres = new List<Tyre>();
}
public ProductTyreSelectorViewModel(IPublishedContent content, CultureInfo culture)
: base(content, culture)
{
}
public ProductTyreSelectorViewModel(IPublishedContent content)
: base(content)
{
}
public IList<Tyre> Tyres { get; set; }
}
This approach will work well perhaps with one to two HTTP GET forms on a given page. If there are multiple forms within in a page, then a good solution will may be to use ChildAction approach. Something I will experiment with further.
Hope this helps!
I am new to Zend and very keen to learn, so I would really appreciate some help and guidance.
I am trying to create a 'method in a class' that will save the session variables of product pages visited by members to a site i.e
i,e examplesite com/product/?producttype= 6
I want to save the number 6 in a session variable. I also do not want to have a global session for the entire site; I just want it for selected pages. So, I guess I have to have Zend_Session::start() on the selected page; but I am not clear how this should be done.
Should I instantiate it in the page view page. i.e products page or do this in the indexAction() method for the products page. I have attempted to instantiate it below but it did not work.
public function rememberLastProductSearched()
{ //my attempt to start a session start for this particular page.
Zend_Session::start();
}
$session->productSearchCategory = $this->_request->getParam('product-search-category');
return" $session->productSearchCategory ";
}
else
{
//echo " nothing there
return " $session->productSearchCategory";
//";
}
}
With the rememberLastProductSearched() method I was trying to get the method to first check whether the user had searched for a new product or just arrived at the page by default. i.e whether he had used the get() action to search for a new product. If the answer is no, then I wanted the system to check whether their had been a previous saved session variable. so in procedural syntax it would have gone like this:
if(isset($_Get['producttype']))
{
//$dbc database connection
$producttype = mysqli_real_escape_string($dbc,trim($_GET['producttype']));
}
else
if(isset($_SESSION['producttype'])){
$producttype = mysqli_real_escape_string($dbc,trim($_SESSION['producttype']));
}
Can you please help me with the Zend/oop syntax. I am totally confused how it should be?
you're asking about simple work flow in an action, it should begin something like:
//in any controller
public function anyAction()
{
//open seesion, start will be called if needed
$session = new Zend_Session_Namespace('products');
//get value
$productCategory = $this->getRequest()->getParam('producttype');
//save value to namespace
$session->productType = $productCategory;
//...
}
now to move this off to a separate method you have to pass the data to the method...
protected function rememberLastProductSearched($productType)
{
//open seesion, start will be called if needed
$session = new Zend_Session_Namespace('products');
$session->productType = $productType;
}
So now if you want to test for presence of a value...
//in any controller
public function anyAction()
{
//open seesion, call the namespace whenever you need to access it
$session = new Zend_Session_Namespace('products');
if (!isset($session->productType)) {
$productCategory = $this->getRequest()->getParam('producttype');
//save value to session
$this->rememberLastProductSearched($productCategory)
} else {
$productCategory = $session->productType;
}
}
That's the idea.
Be mindful of your work flow as it can sometimes be very simple to inadvertently overwrite your session values.
$session = new Zend_Session_Namespace("productSearch");
if ($this->getRequest()->getParam('producttype')) { //isset GET param ?
$session->productType = $this->getRequest()->getParam('producttype');
$searchedProductType = $session->productType;
} else { //take the session saved value
if ($session->productType) {
$searchedProductType = $session->productType;
}
}
//now use $searchedProductType for your query