How do I avoid STANDARD_PRICE_NOT_DEFINED when unit-testing an OpportunityLineItem in Apex v24.0? - salesforce

Apparently a new feature of the Spring '12 / v24.0 release of Apex in Salesforce.com is that unit tests no longer have access to 'real' data -- thus (if I'm understanding the change correctly) a SOQL query will now only retrieve objects that have been inserted during the course of the unit test -- and even that is subject to some limitations.
At any rate this seems to throw OpportunityLineItem testing out the window, because:
It's impossible to insert an OpportunityLineItem without a PriceBookEntryId, BUT
You can't insert a new price-book entry for product X unless you already have a Standard Price Book entry for product X, BUT
There isn't a Standard Price Book in the test data because the Pricebook2 table, like all tables, is effectively empty at the beginning of the unit-test run, AND
There's no way to create a Standard Price Book in Apex
I'm really hoping I got at least one of those four points wrong, but so far no variation on my existing unit-tests has shown any of them to be wrong. Which of course means my unit tests no longer work. This happened literally overnight -- the tests ran fine in my sandbox on Friday, and now they fail.
Am I missing something, or is this a bug in the new SFDC release?

There is new functionality introduced in Summer 14, you can now use Test.getStandardPricebookId() to get the standard pricebook ID without having to set SeeAllData to True.

Firstly, to put your mind at ease, there are no plans ever to deprecate the seeAllData flag. We're not going to pull the rug out from under you. As to the creation of standard price book in an apex test, I'm not sure. There are, I'm sure, several areas where testing without existing data is difficult on the platform today, which is one reason why the seeAllData flag is there. We'll be trying to close those gaps in the next few releases.

I just ran into this, and although your post is old, it's the first result on Google so I thought I'd share what I did.
My basic architecture is a test class that calls a utility class to as a way of creating test data on the fly (there are other ways, this is my habit).
Short version:
set see all data to true
make sure the standard price book is active
add a pricebook entry for the standard price book - flag as active
add a pricebook entry for you test price book - flag as active
Test class:
#isTest (seeAllData=true)
public with sharing class RMA_SelectLineItemsControllerTest {
static testmethod void testBasicObjects() {
Pricebook2 standard = [Select Id, Name, IsActive From Pricebook2 where IsStandard = true LIMIT 1];
if (!standard.isActive) {
standard.isActive = true;
update standard;
}
Pricebook2 pb = RMA_TestUtilities.createPricebook();
Product2 prod = RMA_TestUtilities.createProduct();
PricebookEntry pbe = RMA_TestUtilities.createPricebookEntry(standard,pb,prod);
}
}
The utility method look like this (only showing that around the new PBE):
public static PricebookEntry createPricebookEntry (Pricebook2 standard, Pricebook2 newPricebook, Product2 prod) {
System.debug('***** starting one');
PricebookEntry one = new PricebookEntry();
one.pricebook2Id = standard.id;
one.product2id = prod.id;
one.unitprice = 1249.0;
one.isactive = true;
insert one;
System.debug('***** one complete, ret next');
PricebookEntry ret = new PricebookEntry();
ret.pricebook2Id = newPricebook.id;
ret.product2id = prod.id;
ret.unitprice = 1250.0;
ret.isactive = true;
insert ret;
return ret;
}

Another work around would be to make your trigger be aware of being run in a test using Test.isRunningTest(), but I think this solution misses the point of best practice, which I believe is the whole point of making tests isolated from pre-existing data.
Perhaps Salesforce could make the Pricebook2.isStandard field writeable if code is running in the context of a test, or the specific Standard Price Book record should be given the same status as User and Profile??

Please let me know if anyone has used Test.getStandardPricebookId() and able to insert opportunity line item in test class. I tried this method with below mentioned code but got an error ": STANDARD_PRICE_NOT_DEFINED, No standard price defined for this product: []".
Note: I have seeAllData=false
ID standardPBID = Test.getStandardPricebookId();
PriceBook2 pb = new PriceBook2();
pb.name = 'GEW Water CMS';
pb.isActive=true;
insert pb;
Product2 prod= new Product2();
prod.name='TestProd';
prod.productcode='4568';
prod.isActive=true;
insert prod;
PricebookEntry standardPrice = new PricebookEntry(Pricebook2Id = standardPBID, Product2Id = prod.Id, UnitPrice = 10000, IsActive = true, currencyISOCode='USD' );
PriceBookEntry pbe= new PricebookEntry(pricebook2id=pb.id, product2id=prod.id,unitprice=2000, isActive=true, currencyISOCode='EUR');
insert pbe;
OpportunityLineItem oli = new OpportunityLineItem(OpportunityId = OppList[0].Id, pricebookentryid=pbe.id, UnitPrice = 100, Quantity = 1);
insert oli;

Related

Can somebody explain the error 'System.QueryException: List has no rows for assignment to SObject'?

everyone. Im new to apex, I was trying for code coverage of atleast 75% on my batch apex code through test class. But im getting the error " System.QueryException: List has no rows for assignment to SObject ". Execute method is not covering in the code. Will anyone point out whats wrong with the testclass?
Batch Apex Class:-
Global class BatchApexOnAccount implements Database.Batchable<sObject> {
Global Database.QueryLocator start (Database.BatchableContext Bc){
return Database.getQueryLocator('Select Id, Name from Account');
}
Global void Execute(Database.BatchableContext Bc, List<Account> AccList){
List<Account> AccEmpty = new List<Account>();
for(Account Acc : AccList){
Acc.Name = 'Salesforce';
Acc.Description = 'Update Account ' + system.today();
AccEmpty.add(Acc);
}
Update AccEmpty;
}
Global void Finish (Database.BatchableContext Bc){
}
}
Test class:-
Test code
In your test class you have initiated Account but didn't insert, and in Line 21 you are querying the fields based on that account id which doesn't exists. Because of this you are getting the QueryException.
Your test is weird. You select existing accounts. There are none. You have a piece of batch copy-pasted - why? Even if it'd work - how would you distinguish between what the test did and what the batch did?
You'd have to insert some accounts first. I think the error you get is from test's line 21 but the problems (including "Execute method is not covering in the code") are earlier.
Next time include the test's code too, screenshots are painful to retype.
I think you need something like this (there might be typos!)
private static void testBatch(){
List<Account> accs = new List<Account>{
new Account(Name = 'Unimportant'),
new Account(Name = 'Whatever', Description = 'blah blah...')
};
insert accs;
Test.startTest();
Database.executeBatch(new BatchApexOnAccount());
Test.stopTest();
for(Account a : [SELECT Name, Description FROM Account]){
System.assertEquals('Salesforce', a.Name);
System.assert(a.Description.startsWith('Update account'));
}
}
Edit: another user suggested following edit to my post. I don't agree with it but I'll put it here to preserve the idea and he can always post his own answer with improvements/reasoning.
https://stackoverflow.com/users/6718505/waldemar-liedle
All Assets (sic) in a Loop is a Bad Idea. When no Accounts are Inserted, the Test would still succeed. I would also Check something Outside Loop
List<Account> testAccs = [SELECT Name, Description FROM Account];
System.assertEquals(accs.size(), testAccs.size());
for(Account a : testAccs){
System.assertEquals('Salesforce', a.Name);
System.assert(a.Description.startsWith('Update account'));
}
So...
I don't think it matters because accounts are inserted earlier in the test. You'd need to completely mess the batch job up so it'd start deleting accounts for this query to fail finding any.
normally tests see only data that was inserted during test so it's not like it'll query thousands here (you'd run into other problems like "in test there can be only 1 execute()" so 200 accounts max)
in extreme cases querying all records to a List instead of looping through query (silently calling queryMore() when needed) will eat lots of memory

Why does this get method stop returning correctly?

I am trying to write an app engine application for my university. What I am trying to achieve right now, is to create a method which takes in a Course name, and returns a list of all the CourseYears (think of that as being like a link table e.g. if Maths is the course, and it has Year 1, year 2 and Year 3; MathsYear1, MathsYear2 and MathsYear3 would be the names of the CourseYears).
This is the code for the module (WARING: super dirty code below!):
#ApiMethod(name = "courseYears")
public ArrayList<CourseYear> courseYears(#Named("name") String name){
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query.Filter keyFilter = new Query.FilterPredicate("name", Query.FilterOperator.EQUAL, name);
Query query = new Query("Course").setFilter(keyFilter);
PreparedQuery preparedQuery = datastore.prepare(query);
List<Entity> resultList = preparedQuery.asList(FetchOptions.Builder.withLimit(1));
Course course = ofy().load().type(Course.class).id(resultList.get(0).getKey().getId()).now();
ArrayList<String> courseYearNames = course.getAllCourseYearNames();
System.out.println(course.getName());
ArrayList<CourseYear> courseYears = new ArrayList<CourseYear>();
for(String courseYearName: courseYearNames){
Query.Filter courseNameFilter = new Query.FilterPredicate("name", Query.FilterOperator.EQUAL, courseYearName);
Query query2 = new Query("CourseYear").setFilter(courseNameFilter);
List<Entity> resL = preparedQuery.asList(FetchOptions.Builder.withLimit(1));
System.out.println("test");
CourseYear courseYear = ofy().load().type(CourseYear.class).id(resL.get(0).getKey().getId()).now();
courseYears.add(courseYear);
}
return courseYears;
}
It basically takes a Course name in, applies a filter on all courses to get the corresponding Course object, and then calls getAllCourseYearNames() on the course to get an array list containing all its CourseYears' names. (I would have loved to do this using Keys, but parameterised Objectify keys don't seem to be supported in this version of App Engine).
I then try and get the CourseYears by looping through the arraylist of names and applying the filter for each name. I print "test" each time to see how many times it is looping. Like I said, a super dirty way of doing it.
When I try passing a few course names as a parameters, it loops the correct number of times only once or twice, and after that does not loop at all (doesn't print "test"). I could understand if it never looped, but not doing it correctly once or twice and then never again. It doesn't successfully return a list of CourseYears when it does work, but rather the relevant number of NULLs - I don't know if this is relevant. I believe it successfully retrieves the course every time, as I print the name of the course after loading and it never fails to do this.
If anyone has ANY suggestions for why this may be happening, I would be incredibly grateful to hear them!
Thanks
query2 is never used in your code. You reuse preparedQuery from your previous query, which runs on a different entity kind.

change a db from a certain point in time, when the change doesn't fit the already existing data

I have a model that looks like this:
class Report(models.Model):
updater = models.CharField(max_length=15)
pub_date = models.DateTimeField(auto_add_now=True)
identifier = models.CharField(max_length=100)
... and so on...
There are some more fields but they are irrelevant to the question. Now the site has very simple functions - the users can see older reports and their data, and can edit them or add new ones.
However, the identifier field is actually an integer that symbolizes a log file that is being reported. Most of the times, each report has one log. But sometimes it has more than one. I did it as a CharField because I built the site to replace an older sharepoint 2003 website, where that field was treated as simple text. So I want that in my next version, it would be like it should be, i.e. like this:
class Report(models.Model):
updater = models.CharField(max_length=15)
pub_date = models.DateTimeField(auto_add_now=True)
... and so on...
class Log(models.Model):
report = models.ForeignKey(Report)
identifier = models.IntegerField()
The problem is, since in the old site that field was a CharField, people used this as they liked. Meaning, even if they updated various logs in the same report they just did it like this <logid1>, <logid2>. Sometimes they added some text <logid1> which is related to <logid2>.
So I want to change this, but I don't want to lose all the old data, and I can't fix all those edge cases (the DB contains around 22 thousand reports). I thought about adding this to report:
def disp_id(self):
if self.pub_date < ... #the day I'll do the update
return self.identifier
else:
return ', '.join([log.identifier for log in self.log_set.all()])
But then I'm not really getting rid of the old field now am I? I'm just adding a new one and keeping the original null from a certain date.
As far as I know, what I want to do is impossible. I'm only asking because I know that maybe I'm not the first one to deal with this sort of thing and maybe there is a solution that I'm not aware of.
Hope my explanation is clear enough, thanks in advance!
class Report(models.Model):
updater = models.CharField(max_length=15)
pub_date = models.DateTimeField(auto_add_now=True)
identifier = models.CharField(null=True)
... and so on...
logs = models.ManyToManyField(Log,null=True)
class Log(models.Model):
identifier = models.IntegerField()
Make the above model , and then make a script as follow:
ident_list = []
for reports in Report.objects.all():
identifiers = reports.identifiers.split(',')
for idents in identifiers:
if not idents in ident_list:
log = Log.create(**{'identifier' : int(idents)})
ident_list.append(int(idents))
else:
log = Log.objects.get(identifier = int(idents))
report.log.add(log)
Check the data before removing the column identifiers from the table Report.
Does it solves your purpose now ?

How do I SubmitChanges on multiple tables that are related in LinqToSql?

I'm making a Windows Phone 7.1 application, and I'm having a lot of trouble submitting changes to my database. Here is the structure of the tables in my database:
Day <-1-----*-> TrainingSession <-many-----1-> Sport
So, a single day can have many training sessions, and a training session has one sport. A single sport can naturally be in many different training sessions.
The primary keys look like this:
Day - DateTime
TrainingSession - int (DB generated)
Sport - nvarchar(200)
Sports will simply have attributes sportName, and an iconFileName.
I've set up Associations by putting EntitySet in both Day and Sport, and TrainingSession has EntityRef and EntityRef. I'm not 100% sure if Sport needs the EntitySet, so please correct me if I'm wrong. For the moment, I just hard-coded some sports in my Sport class for testing, and you'll see me retrieving an ObservableCollection to get those out.
Here is how I am trying to create a collection of days with training sessions, each training session having different sports:
public void CreateDay(DateTime date)
{
FitPlanDataContext calendarDatabase = new FitPlanDataContext(FitPlanDataContext.ConnectionString);
DateTime firstDate = new DateTime(date.Year, date.Month, 1);
DayItem dayItem = new DayItem();
dayItem.DateTime = firstDate;
fillTestDayItemWithRandomData(dayItem);
calendarDatabase.DayItems.InsertOnSubmit(dayItem);
calendarDatabase.SubmitChanges();
}
private void fillTestDayItemWithRandomData(DayItem dayItem)
{
ObservableCollection<SportArt> sportArtCollection = SportArtController.GetAllSports();
dayItem.TrainingSessions = new EntitySet<TrainingSession>();
ObservableCollection<TrainingSession> trainingSessionCollection = new ObservableCollection<TrainingSession>();
TrainingSession trainingSession1 = new TrainingSession();
trainingSession1.DayItem = dayItem;
trainingSession1.SportArt = sportArtCollection[1];
trainingSessionCollection.Add(trainingSession1);
TrainingSession trainingSession2 = new TrainingSession();
trainingSession2.DayItem = dayItem;
trainingSession2.SportArt = sportArtCollection[2];
trainingSessionCollection.Add(trainingSession2);
FitPlanDataContext calendarDatabase = new FitPlanDataContext(FitPlanDataContext.ConnectionString);
calendarDatabase.TrainingSessions.InsertAllOnSubmit<TrainingSession>(trainingSessionCollection);
}
This code is not working for me, and it is giving me the following error:
NotSupportedException was Unhandled:
An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported.
Before I got this error, I was also getting NullReferenceExceptions.
I've been looking around for a solution, and I saw some people used Detach or workarounds with Attach, but I havent figured out how I could implement it to my code. Could anyone give me a helping hand with this?
Also, I thought the NullReferenceException could be coming from the fact that I'm not saving any sports to the database, could this be so?
So I messed around with it a lot, and today I finally found the solution I was looking for.
It seems I asked the question wrong. I didn't include the query from the database, which is probably important to add. I actually omitted a lot of the code to keep things simple in my question, but looks like I omitted too much.
Anyways, it turned out the way I setup the database structure was correct, and nothing had to be changed there.
So here's what I did to get it working:
-The call to the method that fills the day with training sessions needed to go after submitting changes about the day. This is because days have training sessions, and I cant save training sessions without the day already in the database.
-I added using statements around the places where I need to use the datacontext instead of just creating an instance of the datacontext with a local variable. This ensures that the datacontext lives only in the scope of the using statment.
(I changed the DateTime of the day to be the date given as the parameter to the method)
public void CreateDay(DateTime date)
{
DayItem dayItem = new DayItem();
dayItem.DateTime = date;
using (FitPlanDataContext calendarDatabase = new FitPlanDataContext(FitPlanDataContext.ConnectionString))
{
calendarDatabase.DayItems.InsertOnSubmit(dayItem);
calendarDatabase.SubmitChanges();
}
fillTestDayItemWithRandomData(dayItem);
}
Then, the changes to the method that fills the day with training sessions go like this:
-I open a using statement where I instantiate a new datacontext. Then I access the database to retrieve a list of all the sports, and also the day that I need to update. I find the day I need to update by dayItemParameter. (Remember that retrieving from the database will give you a collection.)
-I create my new training sessions and fill their properties. Note that the day I retrieved from the database is the value of a training session's property because the training session is a child of day, and needs to know who its parent day is.
-I removed the instantiation of EntitySet because I realized that I already instantiate it in the constructor of the DayItem class.
-Lastly, I add all the new training sessions into a collection, and save them all to the database at once using InsertAllOnSubmit(collection).
private void fillTestDayItemWithRandomData(DayItem dayItemParameter)
{
using (FitPlanDataContext calendarDatabase = new FitPlanDataContext(FitPlanDataContext.ConnectionString))
{
ObservableCollection<SportArt> sportArtCollection;
var sportArts = (from SportArt sportArt in calendarDatabase.SportArts
select sportArt);
sportArtCollection = new ObservableCollection<SportArt>(sportArts);
ObservableCollection<DayItem> dayItemCollection;
var dayItems = (from DayItem dayItem in calendarDatabase.DayItems
where dayItem.DateTime == dayItemParameter.DateTime
select dayItem);
dayItemCollection = new ObservableCollection<DayItem>(dayItems);
DayItem foundDayItem = dayItemCollection[0];
ObservableCollection<TrainingSession> trainingSessionCollection = new ObservableCollection<TrainingSession>();
TrainingSession trainingSession1 = new TrainingSession();
trainingSession1.DayItem = foundDayItem;
trainingSession1.SportArt = sportArtCollection[1];
trainingSessionCollection.Add(trainingSession1);
TrainingSession trainingSession2 = new TrainingSession();
trainingSession2.DayItem = foundDayItem;
trainingSession2.SportArt = sportArtCollection[2];
trainingSessionCollection.Add(trainingSession2);
calendarDatabase.TrainingSessions.InsertAllOnSubmit<TrainingSession>(trainingSessionCollection);
calendarDatabase.SubmitChanges();
}
}
Conclusion:
The main problem I was having was that I was trying to save training sessions to a day that wasn't submitted to the database. The next big problem (that I think many others have) is that reading and updating of an entity has to be in the same datacontext. So, you can't create a datacontext to retrieve a day, then use another datacontext to add a training session to that day (even if you saved the value of the day to a local variable). You need to retrieve the day and save training sessions to it all in the same data context.
At the moment, my application is working, but it is quite sluggish. In this question, I'm asking about just one day, but in my actual program, I'm creating hundreds of days, which means a lot of opening and closing of the database. If anyone has suggestions to how I can
optimize the process, I'm open ears.
I realize and apologize that this post got so long, but writing it helped me to understand the situation with more depth, and I really hope that it'll help others too.

Cannot retrieve user object from foreign key relationships using Linq to Entities statement

I'm trying to retrieve a user object from a foreign key reference but each time I try to do so nothing gets returned...
My table is set up like this:
FBUserID long,
UserID uniqueidentifier
so I have my repository try to get the User when it's provided the FBUserID:
public User getUserByFBuid(long uid)
{
User fbUser = null;
IEnumerable<FBuid> fbUids = _myEntitiesDB.FBuidSet.Where(user => user.FBUserID == uid);
fbUser = fbUids.FirstOrDefault().aspnet_Users;
return fbUser;
}
I've checked that the uid (FBUserID) passed in is correct, I've check that the UserID is matched up to the FBUserID. And I've also checked to make sure that fbUids.Count() > 0...
I've returned fbUids.FirstOrDefault().FBUserID and gotten the correct FBUserID, but any time I try to return the aspnet_Users or aspnet_Users.UserName, etc... I don't get anything returned. (I'm guessing it's getting an error for some reason)
I don't have debugging set up properly so that's probably why i'm having so much troubles... but so far all the checking I've done I've been doing return this.Json(/* stuff returned form the repository */) so that I can do an alert when it gets back to the javascript.
Anyone know why I would have troubles retrieving the user object from a foreign key relationship like that?
Or do you have any suggestions as to finding out what's wrong?
For now, with Entity Framework 1, you don't get automatic delayed loading, e.g. if you want to traverse from one entity to the next, you need to either do an .Include("OtherEntity") on your select to include those entities in the query, or you need to explicitly call .Load("OtherEntity") on your EntityContext to load that entity.
This was a design decision by the EF team not to support automagic deferred loading, since they considered it to be too dangerous; they wanted to make it clear and obvious to the user that he is also including / loading a second set of entities.
Due to high popular demand, the upcoming EF v4 (to be released with .NET 4.0 sometime towards the end of 2009) will support the automatic delayed loading - if you wish to use it. You need to explicitly enable it since it's off by default:
context.ContextOptions.DeferredLoadingEnabled = true;
See some articles on that new feature:
A Look at Lazy Loading in EF4
POCO Lazy Loading
Don't know if this is what you are asking but i do a join like so;
var votes = from av in dc.ArticleVotes
join v in dc.Votes on av.voteId equals v.id
where av.articleId == articleId
select v;
Did this help or am I off base?

Resources