What if you need to spin up the Spring container to run your tests? Perhaps you want to verify that the database schema still operates against your entities and JPA code, or you want to make sure your Spring configuration is valid. Roo has two approaches for you—either you can use the Roo entity test framework and DataOnDemand classes, or build traditional Spring integration tests. Let’s take a look at both approaches, starting with the entity test framework.
9.4.1 Creating entity integration tests
As you saw in chapters 3 and 4, you can create entity integration tests automatically in two ways, either during creation of the entities themselves,
roo> entity jpa --class ~.model.Course --testAutomatically
or later, as a separate action:
roo> test integration --entity ~.model.Course
In either case, Roo creates a scaffolded integration test, named CourseIntegration- Test, and places it within the same package structure as the entity in the Maven src/
test/java directory. It also uses the DataOnDemand framework to scaffold the test data, and perform tests for all entity operations, as shown in figure 9.4.
The testing framework shown in the figure automatically exercises all methods in the Roo entity. The difference between this test and the ones discussed earlier in this chapter is that it runs within the Spring container. The funny thing is how short the Java class is:
227 Testing in-container with Roo
@RooIntegrationTest(entity = Course.class) public class CourseIntegrationTest { @Test
public void testMarkerMethod() { }
}
You’ll do all of the work using the IntegrationTest aspect combined with the Data- OnDemand aspect. The following listing shows a portion of the CourseIntegrationTest _RooIntegrationTest.aj aspect.
privileged aspect CourseIntegrationTest_Roo_IntegrationTest { declare @type: CourseIntegrationTest:
@RunWith(SpringJUnit4ClassRunner.class);
declare @type: CourseIntegrationTest:
@ContextConfiguration(
locations = "classpath:/META-INF/spring/applicationContext.xml") Listing 9.2 The CourseIntegrationTest Roo aspect
userMethod1() userMethod2()...
name listPrice ...
Course.java
persist() update() merge() id version entityManager...
Course_RooEntity.aj
init()
getNewTransientCourse(int id) getRandomCourse() setName(name) setDescription(description) ...
...
CourseDataOnDemand_
RooDataOnDemand.aj CourseDataOnDemand
<<uses>>
CourseIntegrationTest
testPersist() testFindById() testUpdate() testMerge()
dod : CourseDataOnDemand CourseIntegrationTest_
RooIntegrationTest.aj
<<creates>>
Figure 9.4 The integration test and DataOnDemand frameworks
declare @type: CourseIntegrationTest: @Transactional;
@Autowired
private CourseDataOnDemand CourseIntegrationTest.dod;
...
@Test
public void CourseIntegrationTest.testFindCourse() {
org.rooina.coursemanager.model.Course obj = dod.getRandomCourse();
org.junit.Assert.assertNotNull(
"Data on demand for 'Course' failed to initialize correctly", obj);
java.lang.Long id = obj.getId();
org.junit.Assert.assertNotNull(
"Data on demand for 'Course' failed to provide an identifier", id);
obj = org.rooina.coursemanager.model.Course.findCourse(id);
org.junit.Assert.assertNotNull(
"Find method for 'Course' illegally returned null for id '"
+ id + "'", obj);
org.junit.Assert.assertEquals(
"Find method for 'Course' returned the incorrect identifier", id, obj.getId());
} ...
}
As you saw in the previous listing, each method, such as findCourses(), is tested in a separate method, using some simple interactions with the DataOnDemand class and the entity’s API.
Obviously, these tests may require you to do some leg work, pushing in various methods so you can change them.
9.4.2 Testing other Spring beans
For Roo-built services and repositories, any entity integration tests you define with test integration will automatically adjust between testing the entity directly and using the repository or service layer, if you’ve generated one for your entity. This means that you already know how to test Roo-built services and repositories.
Roo doesn't actually support the generation of freestyle tests from the shell. So, to build one, you can either create it using your IDE or use the class command to ini
tialize it:
roo> class --class ~.web.BillingServiceSystemTest ➥
--path SRC_TEST_JAVA
Roo uses Spring’s annotation-driven JUnit 4 test runner in the automated integration tests. You configure this framework by hand as shown here:
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.➥
TransactionConfiguration;
229 Testing in-container with Roo
import org.springframework.transaction.annotation.Transactional;
@ContextConfiguration(locations = ➥
"classpath:/META-INF/spring/applicationContext*.xml")
@RunWith(SpringJUnit4ClassRunner.class) public class BillingServiceSystemTest { @Autowired
private BillingService service;
long studentId;
}
The @ContextConfiguration annotation defines the Spring context files to search for to load your test. Roo stores these in META-INF/spring, so pull in the primary con
text file, applicationContext.xml. That file contains your JPA configuration settings.
Next, you tell JUnit that it has to run under the Spring Framework with your @Run- With annotation.
The test will exercise the BillingService, verifying that an invoice can be created based on student registrations that haven’t yet been paid. The JUnit initialization method initData() configures the test data for your service, so you can use it in mul
tiple tests. It also captures the id of the student that you'll use to generate the invoice:
@Before
public void initData() {
StudentDataOnDemand studentDod = new StudentDataOnDemand();
Student student = studentDod.getRandomStudent();
studentId = student.getId();
CourseDataOnDemand courseDod = new CourseDataOnDemand();
Course course1 = courseDod.getRandomCourse();
course1.setListPrice(new BigDecimal("250.0"));
course course2 = courseDod.getRandomCourse();
course2.setListPRice(new BigDecimal("250.0"));
OfferingDataOnDemand offeringDod = ➥
new OfferingDataOnDemand();
Offering offering1 = offeringDod.getRandomOffering();
offering1.setCourse(course1);
Offering offering2 = offeringDod.getRandomOffering();
offering2.setCourse(course2);
Registration reg = new Registration();
reg.setOffering(offering1);
reg.setAttended(true);
reg.setOffering(offerings.get(0));
reg.setPaymentMade(false);
reg.setStudent(student);
reg.persist();
Registration reg2 = new Registration();
reg.setOffering(offering2);
reg.setCourse(course2);
reg2.setAttended(true);
reg2.setOffering(offerings.get(1));
reg2.setPaymentMade(false);
reg2.setStudent(student);
reg2.persist();
reg2.flush();
}
The testGenerateInvoice() test below exercises the generation of an invoice for the two course offerings the student took. The student should owe $500; you've created two courses, each with a list price of $250:
@Transactional
@Test
public void testGenerateInvoice() {
Integer invoiceId = service.generateInvoice(➥
studentId, startDate, endDate);
Invoice invoice = Invoice.findInvoice(invoiceId);
Assert.assertNotNull(invoice);
Assert.assertEquals(new BigDecimal("500.00"),➥
invoice.getAmount());
}
Now you've seen how you can write tests against live Spring beans such as Roo services and repositories. You can use this technique to test any Spring bean in your container;
just autowire the bean into the test and exercise it as you would in your application code, using asserts to verify behavior.
Now that we’ve discussed how to write integration tests, we should mention that all of the tests introduced so far are rather invasive, looking at the system from the inside.
Testers call these white-box tests.
Equally valuable are tests that look at the system as a black box: tests external to the application that exercise the application as a user would. For those tests, we’ll look at Roo’s support for web testing, using the Selenium automated web testing tool.