NHibernate unit test cases 101 - unit-testing

I found what I thought was a great article by Ayende on creating a simple base test fixture for NHib unit testing with SQLite.
My question here is the code for a test case in concrete test fixture. In EX_1 below, Ayende wraps both the save a fetch in a transaction which he commits, and has a Session.Clear in between. This works, or course, but so does EX_2.
All things equal I'd prefer the more compact, readable EX_2. Any thoughts on why the additional code in EX_1 is worth a bit of clutter?
Cheers,
Berryl
==== EX_1 =====
[Fact]
public void CanSaveAndLoadBlog_EX_1()
{
object id;
using (var tx = session.BeginTransaction())
{
id = session.Save(new Blog
{
AllowsComments = true,
CreatedAt = new DateTime(2000,1,1),
Subtitle = "Hello",
Title = "World",
});
tx.Commit();
}
session.Clear();
using (var tx = session.BeginTransaction())
{
var blog = session.Get<Blog>(id);
Assert.Equal(new DateTime(2000, 1, 1), blog.CreatedAt);
Assert.Equal("Hello", blog.Subtitle);
Assert.Equal("World", blog.Title);
Assert.True(blog.AllowsComments);
tx.Commit();
}
}
==== EX_2 =====
[Fact]
public void CanSaveAndLoadBlog_EX_2()
{
var id = session.Save(new Blog
{
AllowsComments = true,
CreatedAt = new DateTime(2000, 1, 1),
Subtitle = "Hello",
Title = "World",
});
var fromDb = session.Get<Blog>(id);
Assert.Equal(new DateTime(2000, 1, 1), fromDb.CreatedAt);
Assert.Equal("Hello", fromDb.Subtitle);
Assert.Equal("World", fromDb.Title);
Assert.True(fromDb.AllowsComments);
}

I believe with NHibernate it is encouraged to use transactions even when you you're only querying. Check this article http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions.
Also your EX_2 code might not hit the database depending on what type of primary key you're using. If you're using Identity key that autoincrement, NHibernate will hit the database and get a primary key, but if you using guid, guid.comb, or hilo, you won't hit the database at all. So your Get would be grabbing what NHibernate has cached in memory unless you do a commit changes and then clear the session so you know you got nothing in memory.

Related

Grails Unit Testing with Composite Keys

I'm trying to unit test a method where an object with a composite key is being inserted into the database. Whenever I run my unit test for this, I get the following.
Failure: |
testTransaction(com.myapp.foo.TestServiceSpec)
|
Condition not satisfied:
Transaction.count() == 1
| |
0 false
at com.myapp.foo.TestServiceSpec.testTransaction(TestServiceSpec.groovy:166)
If I remove the composite key code and nothing else from my domain class, the test passes.
This is my Domain Class, Transaction.groovy:
class Transaction implements Serializable {
String timestamp
String source
String tableName
String fieldName
Integer changeNumber
String fieldValue
String objectId
static mapping = {
id composite: ["timestamp", "source", "tableName", "fieldName", "changeNumber"], generator: 'assigned'
}
boolean equals(other) {
if (!(other instanceof Transaction)) {
return false
}
other.timestamp == timestamp && other.source == source && other.id == id && other.tableName == tableName && other.fieldName == fieldName && other.changeNumber == changeNumber
}
int hashCode() {
def builder = new HashCodeBuilder()
builder.append timestamp
builder.append source
builder.append tableName
builder.append fieldName
builder.append changeNumber
builder.toHashCode()
}
}
This is the code that's being tested:
def response = [code: 'OK']
def transaction = new Transaction()
transaction.timestamp = (new Date()).format("yyyy-MM-dd HH:mm:ss.SSS")
transaction.source = "APP"
transaction.tableName = "TABLE_NAME"
transaction.fieldName = "FIELD_NAME"
transaction.fieldValue = "FIELD_VALUE"
transaction.objectId = "OBJECT_ID"
def changeNumber = Transaction.createCriteria().get {
eq("objid", currentTransaction.objid)
eq("tableName", currentTransaction.tableName)
projections {
max("changeNumber")
}
}
currentTransaction.changeNumber = (changeNumber ?: 0) + 1
if(!transaction.save()) {
transaction.errors.each {
println it
}
response = [code: 'error transaction', status: 500]
}
return response
And finally, here's my unit test code:
void testTransaction() {
when:
def response = testService.loadTransaction() // Creates transaction row
then:
assert response == [code: 'OK']
assert Transaction.count() == 1
}
The domain structure was defined by another party, and I can't change it in any way, so the composite key is a must. Unfortunately many classes in this app use composite keys, so if I can't unit test them, a lot of my code can't be covered by unit testing. Any info to get me going in the right direction would be great.
Don't use unit tests to test persistence.
Unit tests have a GORM implementation, but it isn't backed by a database, only a ConcurrentHashMap. It's pretty good, but it should only ever be used to avoid having to mock the persistence layer when unit testing other artifact types like controllers. If you want to test persistence, use a database.
Otherwise, you'll see funky issues like this, and similar issues like false negatives or worse - false positives where a test passes that shouldn't, leaving bugs in your "well-tested" code.

mock datareader failing on second call

In the test below, the mocked datareader returns the desired value the first time, but then returns the same value when the index should be 1.
Am I misusing the dataReader or Rhino stub syntax? What is the fix?
Cheers,
Berryl
failing test
[Test]
public void NullSafeGet_GetsBothProperties()
{
var sessionImplementor = MockRepository.GenerateStub<ISessionImplementor>();
var userType = new DateRangeUserType();
var reader = MockRepository.GenerateStub<IDataReader>();
var start = new DateTime(2011, 6, 1);
var end = new DateTime(2011, 7, 1);
reader.Stub(x => x[0]).Return(start);
reader.Stub(x => x[1]).Return(end); ***<==== returns Jun 1 instead of Jul1
var result = userType.NullSafeGet(reader, userType.PropertyNames, sessionImplementor, null);
Assert.That(result, Is.EqualTo(new DateRange(start, end, DateRange.MaxSupportedPrecision)));
}
Expected: <6/1/2011 12:00 AM - 7/1/2011 12:00 AM>
But was: <6/1/2011 12:00 AM - 6/1/2011 12:00 AM>
SUT (NHib CompositeUserType method)
public override object NullSafeGet(IDataReader dr, string[] names, ISessionImplementor session, object owner) {
if (dr == null) return null;
var foundStart = (DateTime)NHibernateUtil.DateTime.NullSafeGet(dr, names[0], session, owner);
var foundEnd = (DateTime)NHibernateUtil.DateTime.NullSafeGet(dr, names[1], session, owner);
var precision = DateRange.MaxSupportedPrecision;
var startDp = _getDatePoint(foundStart, precision);
var endDp = _getDatePoint(foundEnd, precision);
return new DateRange(startDp, endDp, precision);
}
You are not mocking everything that is called by NHibernate. This is roughly what NHibernate does with a reader:
...
int index = reader.GetOrdinal(name);
...
if (reader.IsDBNull(index)) {
return null;
} else {
...
val = rs[index];
...
}
Stub generated by Rhino will return 0 in response both GetOrdinal calls and it this is why it will return June1 both times. You can try to fix it by mocking GetOrdinal as well as indexer. Like this:
var reader = MockRepository.GenerateStub<IDataReader>();
var start = new DateTime(2011, 6, 1);
var end = new DateTime(2011, 7, 1);
reader.Stub(x => x.GetOrdinal(userType.PropertyNames[0])).Return(0);
reader.Stub(x => x.GetOrdinal(userType.PropertyNames[1])).Return(1);
reader.Stub(x => x[0]).Return(start);
reader.Stub(x => x[1]).Return(end);
But it might be worth reconsidering whether you really need to unit test UserType. It does not have a lot of responsibility other than calling NHibernate. Unit testing this class requires you to mock type you don't own (MS IDataReader). What's even worse is that this mock is used by another thirdparty (NHibernate). Essentially you need to look at NHibernate source code (which is what I did) to create a correct stub. Take a look at this article. It goes into a lot more details about why you should avoid mocking types that you don't own. You may be better off writing integration test for this class, using in-memory sqlite database.

Mock IDbDataAdapter Fill Method With Moq

I have an object that reads data from an Excel file using, which takes a IDbConnection, IDbDataAdapter and an IDbCommand. I use the adapters fill method to populate a table with data, and this is how I am currently mocking it:
[TestCase]
public void TestReadCellsFromSpreadsheetReadsSuccessfully()
{
var cells = new List<ReportData>
{
new ReportData { CellId = 1, ExcelCellLocation = "A1"},
new ReportData { CellId = 2, ExcelCellLocation = "A2"},
new ReportData { CellId = 3, ExcelCellLocation = "A3"},
new ReportData { CellId = 4, ExcelCellLocation = "A4"}
};
_mockAdapter.Setup(a => a.Fill(It.IsAny<DataSet>()))
.Callback((DataSet ds) =>
{
if (ds.Tables["Table"] == null)
{
ds.Tables.Add("Table");
ds.Tables["Table"].Columns.Add(new DataColumn());
}
var row = ds.Tables["Table"].NewRow();
row[0] = "Test";
ds.Tables["Table"].Rows.Add(row);
});
var excelReader = new ExcelReader(_mockConnection.Object, _mockAdapter.Object, _mockCommand.Object);
excelReader.ReadCellsFromSpreadsheet("Deal Summary", cells);
_mockCommand.VerifySet(c => c.CommandText = It.IsAny<string>(), Times.Exactly(cells.Count));
_mockAdapter.VerifySet(a => a.SelectCommand = _mockCommand.Object, Times.Exactly(cells.Count));
_mockAdapter.Verify(a => a.Fill(It.IsAny<DataSet>()), Times.Exactly(cells.Count));
}
This implementation works, but I feel like I'm doing too much to Mock the adapter... is there a better way to do this?
Do not pass those 3 objects as parameters. Instead pass IDataReader, IDataProvider or sth like that that returns data. Then You just mock this object. And you don't need reference to System.Data in project containing ExcellReader.
And two other things I don't like about your code.
Why TestCase instead of Test?
Are you sure you want to create command and fill dataset for each column separately? (but maybe I don't understand your code)
In general, I have some rules about data access:
Write a simple class that wraps all the data access logic, so that other classes don't have to deal with DataAdapters and all that crap.
When you write unit tests, don't mock out DataAdapters; instead just mock out the wrapper classes that you just created.
Make the data access wrapper logic so simple that it doesn't really need to be unit tested. If it DOES need to be tested, then write integration tests that hit a small sample database.

How to test a Grails Service that utilizes a criteria query (with spock)?

I am trying to test a simple service method. That method mainly just returns the results of a criteria query for which I want to test if it returns the one result or not (depending on what is queried for).
The problem is, that I am unaware of how to right the corresponding test correctly. I am trying to accomplish it via spock, but doing the same with any other way of testing also fails.
Can one tell me how to amend the test in order to make it work for the task at hand?
(BTW I'd like to keep it a unit test, if possible.)
The EventService Method
public HashSet<Event> listEventsForDate(Date date, int offset, int max) {
date.clearTime()
def c = Event.createCriteria()
def results = c {
and {
le("startDate", date+1) // starts tonight at midnight or prior?
ge("endDate", date) // ends today or later?
}
maxResults(max)
order("startDate", "desc")
}
return results
}
The Spock Specification
package myapp
import grails.plugin.spock.*
import spock.lang.*
class EventServiceSpec extends Specification {
def event
def eventService = new EventService()
def setup() {
event = new Event()
event.publisher = Mock(User)
event.title = 'et'
event.urlTitle = 'ut'
event.details = 'details'
event.location = 'location'
event.startDate = new Date(2010,11,20, 9, 0)
event.endDate = new Date(2011, 3, 7,18, 0)
}
def "list the Events of a specific date"() {
given: "An event ranging over multiple days"
when: "I look up a date for its respective events"
def results = eventService.listEventsForDate(searchDate, 0, 100)
then: "The event is found or not - depending on the requested date"
numberOfResults == results.size()
where:
searchDate | numberOfResults
new Date(2010,10,19) | 0 // one day before startDate
new Date(2010,10,20) | 1 // at startDate
new Date(2010,10,21) | 1 // one day after startDate
new Date(2011, 1, 1) | 1 // someday during the event range
new Date(2011, 3, 6) | 1 // one day before endDate
new Date(2011, 3, 7) | 1 // at endDate
new Date(2011, 3, 8) | 0 // one day after endDate
}
}
The Error
groovy.lang.MissingMethodException: No signature of method: static myapp.Event.createCriteria() is applicable for argument types: () values: []
at myapp.EventService.listEventsForDate(EventService.groovy:47)
at myapp.EventServiceSpec.list the Events of a specific date(EventServiceSpec.groovy:29)
You should not use unit tests to test persistence - you're just testing the mocking framework.
Instead, move the criteria query to an appropriately named method in the domain class and test it against a database with an integration test:
class Event {
...
static Set<Event> findAllEventsByDay(Date date, int offset, int max) {
...
}
}
class EventService {
Set<Event> listEventsForDate(Date date, int offset, int max) {
...
return Event.findAllEventsByDay(date, offset, max)
}
}
If there's still value in having the service method as a wrapper (e.g. if it implements some business logic above and beyond the database query), it will now be easy to unit test since it's trivial to mock out the static domain class method call:
def events = [new Event(...), new Event(...), ...]
Event.metaClass.static.findAllEventsByDay = { Date d, int offset, int max -> events }
And that's appropriate since you're testing how the service uses the data it receives and assuming that the retrieval is covered in the integration tests.
Criteria queries are not supported in unit tests. From the mockDomain documentation:
[T]he plugin does not support the mocking of criteria or HQL queries. If you use either of those, simply mock the corresponding methods manually (for example with mockFor() ) or use an integration test with real data.
You'll have to make your test an integration test. You'll see that the exception goes away if you move the test from the test/unit folder to the test/integration folder.
There is some work being done on criteria support in unit tests, and if you're feeling adventurous, you can try it out today. See this mailing list discussion of the DatastoreUnitTestMixin.

SubSonic GetPaged method - different index needed in unit test

I'm using SubSonic 3, and am using the ActiveRecord approach. I have a simple query in my controller:
var posts = from p in Post.GetPaged(page ?? 0, 20)
orderby p.Published descending
select p;
The "page" variable is defined as an nullable int (int?).
Here's the problem, when I run the following test, it works fine and passes:
[Test]
public void IndexWithNullPageShouldLoadFirstPageOfRecentPosts()
{
//act
var testPosts = new List<Post>()
{
new Post() {PostID = 1, BlogID = 1, Published = null, Body = "1"},
new Post() {PostID = 2, BlogID = 1, Published = DateTime.Now, Body = "2"},
new Post() {PostID = 3, BlogID = 1, Published = DateTime.Now.AddDays(1), Body = "3"}
};
Post.Setup(testPosts);
//act
var result = controller.Index(null) as ViewResult;
//assert
Assert.IsNotNull(result);
var home = result.ViewData.Model as HomeViewModel;
Assert.IsInstanceOf(typeof(HomeViewModel), home, "Wrong ViewModel");
Assert.AreEqual(3, home.Posts.Count());
//make sure the sort worked correctly
Assert.AreEqual(3, home.Posts.ElementAt(0).PostID);
Assert.AreEqual(2, home.Posts.ElementAt(1).PostID);
Assert.AreEqual(1, home.Posts.ElementAt(2).PostID);
}
However, when I launch the website, it doesn't return any records (and yes, there are records in the live database). Both ways have the "page" variable set to null. I found that if I change the index in "GetPaged" method to 1 instead of 0 - then records are returned on the website... however, as soon as I do that - my tests don't pass anymore. All the documentation I've seen shows that the GetPaged index is zero-based... so I'm a bit confused here.
Any ideas?
Thanks,
Chad
This seems to be a bug/inconsistency in the way GetPaged works with ActiveRecord. As you've already worked out it's using a 1 based index instead of a 0 based index in the ActiveRecord repository. Can you please log this as an issue at github