Hi John,
I can only give you my personal advice.
You have a few issues with your test.
1. What are you testing?
2. Why are you testing a private member?
3. Why can't you instantiate a DataMarshaller Normally?
I personally would refactor the code and make the code clearer, but in any case once you get this method out of the way, the path to testing the rest of your system is clear, because you can mock the SaveFeeIncome() method.
In any case we can create helper methods.Here is a way to do it that I think is clearer. I am using Enterprise Features.
// create a fake DataMarshaller - if this was possible just
// do a new DataMarshaller() and set the correct properties.
private DataMarshaller CreateFakeDataMarshaller(bool isDirty, DataMarshallerState state,
bool containsData)
{
DataMarshaller fakeDataMarshaller =
(DataMarshaller)RecorderManager.CreateMockObject(typeof(DataMarshaller ));
using (RecordExpectations rec = RecorderManager.StartRecording())
{
// this is a stub we don't really care if these are called or not
// if it was possible we would just create an instance
rec.DefaultBehavior.RepeatAlways()
rec.ExpectAndReturn(fakeIncomeFee.IsDirty,isDirty);
rec.ExpectAndReturn(fakeIncomeFee.State,state);
rec.ExpectAndReturn(fakeIncomeFee.ContainsData,containsData);
}
return fakeDataMarshaller;
}
If you where using Visual Studio Unit Test, this accessor would be created automatically
private string Call_Fee_SaveFeeIncome(DataMarshaller fee)
{
return (string)typeof(Fees).GetMethod("SaveFeeIncome",
BindingFlags.NonPublic | BindingFlags.Static).Invoke(null,
new object[]{fee});
}
Here is the test
[Test]
public void SaveFeeIncomeGetsDeletedWhenStateDeletedAndHasData()
{
// setup our fake income fee
DataMarshaller fakeIncomeFee = CreateFakeDataMarshaller(
true /*dirty */,DataMarshallerState.Deleted,true/*contains data*/);
// stub connections
using (RecordExpectations rec = RecorderManager.StartRecording())
{
// this is a stub we don't really care if these are called or not
rec.DefaultBehavior.RepeatAlways()
IDbConnection fakeConnection =
EBSShared.CreateTransaction().Connection;
TribalTech.EBS.Process.Shared.CleanupTransaction();
}
// make sure that delete gets called and it passes our income fee
// no error is returned
using (RecordExpectations rec = RecorderManager.StartRecording())
{
TribalTech.EBS.Process.Deletion.Delete(fakeIncomeFee);
rec.Return("").CheckArguments();
}
// Call our private method.
string returnValue =Call_Fee_SaveFeeIncome(fakeIncomeFee)
// Test the result
Assert.AreEqual(returnValue, string.Empty);
MockManager.Verify();
}
Of course you can then test that the connection is always closed and that errors are handled correctly and that the insert/update is ok.
You then know that this method works well and can mock out the database by mocking SaveFeeIncome() for all the other tests.
You might find an urge to refactor your code, and to throw errors instead of returning them.
Your code might look like this:
private static void SaveFeeIncome(DataMarshaller feeIncome)
{
if (ShouldDelete(feeIncome))
{
try {
Delete(feeIncome);
} catch (Exception exception) // are you sure you want to catch all exceptions?
{
throw new SaveException(saveErrorMsgTemplate,feeIncome,exception);
}
}
// Insert and Update code omitted
}
private bool ShouldDelete(DataMarshaller feeIncome)
{
return feeIncome.IsDirty &&
feeIncome.State == DataMarshallerState.Deleted &&
feeIncome.ContainsData;
}
private void Delete(DataMarshaller feeIncome)
{
IDbTransaction trans = EBSShared.CreateTransaction();
IDbConnection conn = trans.Connection;
try {
TribalTech.EBS.Process.Deletion.Delete(feeIncome);
} finally
{
TribalTech.EBS.Process.Shared.CleanupTransaction(trans, conn, false);
}
}