In Grails 1.3.7, I have a code that filters objects by ID and I need to test that
The domain class has a sequence
static mapping = {
id generator: 'sequence', params[sequence: 'seq_shipping_service']
}
In the test, the object is created several times and I need the identifier to be 11 in all the tests and even though, it deletes the whole database between every test, it doesn't reset the sequence. So I would get a superior ID
foo = createFoo()
foo.id = 11l
foo.save () //This gets error
My ideas are
1) Reset somehow the id sequence so it's everytime the same number between tests
2) Set somehow the id
I don't know if I make myself clear
Personally, if I need to do something like this I:
create an object mother to spawn all required objects (i.e. foo.id == 11)
use Groovy SQL to insert data into database. In your case:
public class BlahBlahTest extends Specification {
DataSource dataSource
def setup () {
new Sql(dataSource).exec ("insert into blablah (11, ...)")
}
...
Obviously you could do anything else there - for example recreate the sequence to start with 11.
Related
I am trying to test some methods within a Domain object, the code seems to execute (based on log) but the assertions fail.
The code being tested (extendDates), is working, I am just adding the unit tests now.
I assume I am doing something wrong in the mocking.The following is a simplified version of my code. Based on the log output, the assertion should pass.
class EventDate{
Date startDate
Date endDate
belongsTo = [Appointments owner]
static constraints = {
endDate(nullable:true, blank:false)
startDate(nullable:false, blank:false)
}
}
class Appointments {
hasMany = [ eventDates: EventDate]
belongsTo = [ customer: Customer ]
def extendDates(start,end){
//some logic on eventDates...
EventDate(startDate:start,endDate:end, owner:this).save(flush:true,failOnError:true);
}
}
#TestFor(Appointments)
#Mock([EventDate])
class AppointmentsTests {
void testDateExtend(){
assertTrue domain != null
assertTrue domain instanceof Appointments
//Log indicates the correct execution and creation of event
domain.extendDates(new Date(),null)
//following returns NullPointerException
assertTrue domain.eventDates.size() == 1
}
}
In your example you test for
if (create_new)
The variable "create_new" is never set and would therefore test false using groovy truth logic, thus never executing the if statement.
The if statement never adds anything to the "eventDates" property of Appointments, this would also mean that the assertion would fail.
I'm thinking your example is incomplete and therefore cannot help you until you expand it.
Yes, you will get NullPointerException in the assert condition. The reason being that, you are creating instance of EventDate in the extendDates method but you are not really adding it to the eventDates list in Appointments domain.
So, you have to modify your that method something like:
// Initialize with empty array to avoid NPE
List<EventDate> eventDates = []
static hasMany = [ eventDates: EventDate]
def extendDates(start, end) {
EventDate instance = EventDate(start, end).save()
// And add it to the list of events
this.addToEventDates(instance)
this.save(flush: true)
}
Now, your test case should work your assert condition.
(Also, looks like you have not added nullable constraint in end but passing the null value while creating instance of EventDate, may be not included in sample code)
Let's say I have the following method in a service:
private void deleteItems(List<Item> itemsToDelete) {
def sql = new Sql(dataSource)
itemsToDelete?.each { Item item ->
sql.execute("DELETE FROM owner_item WHERE item_id = ${item.id}")
item.delete(flush: true, failOnError: true)
flushDatabaseSession();
}
}
How do I create a test for this method in the ItemServiceSpec? When I try it, I get either a DataSource "Must specify a non-null Connection" error or a nullPointerException on sql.
This is my existing test.
#TestFor(ItemService)
#Mock([Item])
#Build([Item])
class SubjectServiceSpec extends Specification {
...
def "delete items"() {
given:
Item item1 = Item.build().save(flush: true)
Item item2 = Item.build().save(flush: true)
Item.count() == 2
DataSource mockDataSource = Mock()
service.dataSource = mockDataSource
1 * deleteItems
when:
service.deleteItems([item1, item2])
then:
Item.count() == 0
}
}
What you are trying to do here, is to mock a dependency (DataSource) of a dependency (Sql). This normally leads to a situation, where you a not 100% aware of how the Sql interacts with the DataSource Object. If Sql changes private interaction with the Datasource in a Version Update, you have to deal with the situation.
Instead of mocking a dependency of a dependency you should the Sql Class directly. For this, the sql has to be some kind of explicit dependency that you can receive via DI or a method parameter. In this case you can just mock the execute call like so (choosen the way of a Expando-Mock, but you could also use Map or the Mock Stuff from Spock):
given:
def sqlMock = new Expando()
sqlMock.execute = { return 'what ever you want or nothing, because you mock a delete operation' }
service.sql = sqlMock
when:
service.deleteItems([item1, item2])
then:
assertItemsAreDeletedAndTheOwnerAsWell()
Thinking about the whole testcase, there a two major problems in my opinion.
The first one is, when you ask yourself what kind of certainty do you really get here by mocking out the whole sql stuff. In this case, the only thing that you are doing here is to interact with the db. When you mock this thing out, then there is nothing anymore that you could test. There is not many conditional stuff or anything that should be backed up by a unit test. Due to this, I would suggest to write only integration spec for this test-case where you have something like a H2DB for testing purposes inplace.
The second thing is, that you actually don't need the Sql Manipulation at all. You can configure GORM and Hibernate in a way do a automatic and transparent deletion of the owner of the item, if the item is deleted. For this, look at the docs (especially the cascade part) from GORM or directly in the Hibernate docs.
To sum it up: use cascade: 'delete' together with a proper integration test and you have a high amount of certainty and less boilerplate code.
I am trying to be a good little programmer and set up Unit tests for my Grails 2.2.3 app. The unit tests that use GORM's injected .save() method are apparently not persisting to the mock test DB. For an example, here is what one test consists of:
#TestFor(TermService)
#Mock(Term)
class TermServiceTests {
void testTermCount() {
def t = new Term(code: "201310").save(validate: false, flush: true, failOnError: true)
println "Printing Term: " + t.toString()
assert 1 == Term.count() // FAILS
assert service.isMainTerm(t) // FAILS
}
}
I did a println that ends up printing Printing Term: null, meaning the Term did not save and return a Term instance. The first assertion is false with Term.count() returning 0.
Does anyone know why this might be? I have a mock Term and TermService (via the TestFor annotation, I believe), so I'm not quite sure why this wouldn't work. Thanks!
Edit: Here is my Term class.
class Term {
Integer id
String code
String description
Date startDate
Date endDate
static mapping = {
// Legacy database mapping
}
static constraints = {
id blank: false
code maxSize: 6
description maxSize: 30
startDate()
endDate()
}
}
Looks like id generator is assigned since you have mentioned about using legacy database. Plus id is not bindable by default in domain class (map construct won't work for id). So, I think you have to end up using like below:
def t = new Term(code: "201310")
t.id = 1
t.save(...)
I have a Schedulable class which will get called once per night. I have run the code anonymously and everything works as it should. The problem I am having is that I cannot get proper test coverage on it! I have written a test class that I believe should work, but for some reason any lines within my for-loops are not being covered.
I assume that it is because no data is being returned from these queries, however there are thousands of records that should be returned. I have run the queries on the production environment without any issues.
Is there a separate process for running queries in a schedulable class?
Here's part of my class:
global class UpdateUnitPrice implements Schedulable{
global void execute(SchedulableContext sc){
// OwnerId -> List of Strings with Row Contents
Map<Id,Map<Id,Map<String,String>>> updateContainer = new Map<Id,Map<Id,Map<String,String>>>{}; // Covered
List<Id> ownerContainer = new List<Id>{}; // Covered
String EmailMessage; // Covered
String EmailLine; // Covered
String EmailAddedLines; // Covered
String CurrentEmailLine; // Covered
String NewEmailLine; // Covered
List<Id> opportunityList = new List<Id>{}; // Covered
for(Opportunity thisOpp :[SELECT Id,Name FROM Opportunity WHERE Order_Proposed__c = null])
{
// Thousands of records should be returned
opportunityList.add(thisOpp.Id); // NOT COVERED!!
}
List<OpportunityLineItem> OppLineItemList = new List<OpportunityLineItem>{}; // Covered
for(OpportunityLineItem thisOppProd : [SELECT Id,OpportunityId,Opportunity.OwnerId,Opportunity.Name,Product_Name__c,UnitPrice,ListPrice
FROM OpportunityLineItem
WHERE OpportunityId IN :opportunityList
AND UnitPrice_lt_ListPrice__c = 'True'
ORDER BY OpportunityId ASC])
{
. . . // NO LINES COVERED WITHIN THIS LOOP
}
. . .
}
}
Here's my test class:
#isTest
private class UpdateUnitPriceTest {
static testMethod void myUnitTest() {
Test.startTest();
// Schedule the test job
String jobId = System.schedule('UpdateUnitPrice','0 0 0 3 9 ? 2022',new UpdateUnitPrice());
// Get the information from the CronTrigger API object
CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, NextFireTime FROM CronTrigger WHERE id = :jobId];
// Verify the expressions are the same
System.assertEquals(ct.CronExpression,'0 0 0 3 9 ? 2022');
// Verify the job has not run
System.assertEquals(0, ct.TimesTriggered);
// Verify the next time the job will run
System.assertEquals('2022-09-03 00:00:00', String.valueOf(ct.NextFireTime));
Test.stopTest();
}
}
Am I supposed to specifically reference something within these for-loops for them to fire? This Class should be able to just run everything on it's own without inserting records for testing. What am I missing?
Thanks in advance for any help given!
There was a lovely feature added to API 24.0 which requires test classes to include a small (new) line of code in order to view queried data. I have no idea why this was implemented, but it sure did trip me up.
For our test classes to run properly, they now must have the following at the top:
#isTest (SeeAllData = true)
Previously, all that was needed was:
#isTest
You can read more on test classes and this new "feature" here: Apex Test Class Annotations
I am having an issue with EF 4.1 using "Code First". Let me setup my situation before I start posting any code. I have my DBContext class, called MemberSalesContext, in a class library project called Data.EF. I have my POCOs in a seperate class library project called Domain. My Domain project knows nothing of Entity Framework, no references, no nothing. My Data.EF project has a reference to the Domain project so that my DB context class can wire up everything in my mapping classes located in Data.EF.Mapping. I am doing all of the mappings in this namespace using the EntityTypeConfiguration class from EntityFramework. All of this is pretty standard stuff. On top of Entity Framework, I am using the Repository pattern and the Specification pattern.
My SQL Server database table has a composite primary key defined. The three columns that are part of the key are Batch_ID, RecDate, and Supplier_Date. This table as an identity column (database generated value => +1) called XREF_ID, which is not part of the PK.
My mapping class, located in Data.EF.Mapping looks like the following:
public class CrossReferenceMapping : EntityTypeConfiguration<CrossReference>
{
public CrossReferenceMapping()
{
HasKey(cpk => cpk.Batch_ID);
HasKey(cpk => cpk.RecDate);
HasKey(cpk => cpk.Supplier_Date);
Property(p => p.XREF_ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("wPRSBatchXREF");
}
}
My MemberSalesContext class (inherits from DBContext) looks like the following:
public class MemberSalesContext : DbContext, IDbContext
{
//...more DbSets here...
public DbSet<CrossReference> CrossReferences { get; set; }
//...more DbSets here...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
//...more modelBuilder here...
modelBuilder.Configurations.Add<CrossReference>(new CrossReferenceMapping());
//...more modelBuilder here...
}
}
I have a private method in a class that uses my repository to return a list of objects that get iterated over. The list I am referring to is the outermost foreach loop in the example below.
private void CloseAllReports()
{
//* get list of completed reports and close each one (populate batches)
foreach (SalesReport salesReport in GetCompletedSalesReports())
{
try
{
//* aggregate sales and revenue by each distinct supplier_date in this report
var aggregates = BatchSalesRevenue(salesReport);
//* ensure that the entire SalesReport breaks out into Batches; success or failure per SalesReport
_repository.UnitOfWork.BeginTransaction();
//* each salesReport here will result in one-to-many batches
foreach (AggregateBySupplierDate aggregate in aggregates)
{
//* get the batch range (type) from the repository
BatchType batchType = _repository.Single<BatchType>(new BatchTypeSpecification(salesReport.Batch_Type));
//* get xref from repository, *if available*
//* some will have already populated the XREF
CrossReference crossReference = _repository.Single<CrossReference>(new CrossReferenceSpecification(salesReport.Batch_ID, salesReport.RecDate, aggregate.SupplierDate));
//* create a new batch
PRSBatch batch = new PRSBatch(salesReport,
aggregate.SupplierDate,
BatchTypeCode(batchType.Description),
BatchControlNumber(batchType.Description, salesReport.RecDate, BatchTypeCode(batchType.Description)),
salesReport.Zero_Sales_Flag == false ? aggregate.SalesAmount : 1,
salesReport.Zero_Sales_Flag == false ? aggregate.RevenueAmount : 0);
//* populate CrossReference property; this will either be a crossReference object, or null
batch.CrossReference = crossReference;
//* close the batch
//* see PRSBatch partial class for business rule implementations
batch.Close();
//* check XREF to see if it needs to be added to the repository
if (crossReference == null)
{
//*add the Xref to the repository
_repository.Add<CrossReference>(batch.CrossReference);
}
//* add batch to the repository
_repository.Add<PRSBatch>(batch);
}
_repository.UnitOfWork.CommitTransaction();
}
catch (Exception ex)
{
//* log the error
_logger.Log(User, ex.Message.ToString().Trim(), ex.Source.ToString().Trim(), ex.StackTrace.ToString().Trim());
//* move on to the next completed salesReport
}
}
}
All goes well on the first iteration of the outer loop. On the second iteration of the outer loop, the code fails at _repository.UnitOfWork.CommitTransaction(). The error message returned is the following:
"The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges."
In this situation, the database changes on the second iteration were not committed successfully, but the changes in the first iteration were. I have ensured that objects in the outer and inner loops are all unique, adhering to the database primary keys.
Is there something that I am missing here? I am willing to augment my code samples, if it proves helpful. I have done everything within my capabilities to troubleshoot this issue, minus modifying the composite primary key set on the database table.
Can anyone help??? Much thanks in advance! BTW, sorry for the long post!
I am answering my own question here...
My issue had to do with how the composite primary key was being defined in my mapping class. When defining a composite primary key using EF Code First, you must define it like so:
HasKey(cpk => new { cpk.COMPANYID, cpk.RecDate, cpk.BATTYPCD, cpk.BATCTLNO });
As opposed to how I had it defined previously:
HasKey(cpk => cpk.COMPANYID);
HasKey(cpk => cpk.RecDate);
HasKey(cpk => cpk.BATTYPCD);
HasKey(cpk => cpk.BATCTLNO);
The error I was receiving was that the ObjectContext contained multiple elements of the same type that were not unique. This became an issue in my UnitOfWork on CommitTransaction. This is because when the mapping class was instanciated from my DBContext class, it executed 4 HasKey statements shown above, with only the last one for property BATCTLNO becoming the primary key (not composite). Defining them inline, as in my first code sample above, resolves the issue.
Hope this helps someone!