I'm trying to wrap my head around unit testing, and I've encountered a behavior that I'm unsure of:
"Can Backup Inventory"
Basically, the "Inventory" table is copied to the "InventoryHistory" table, and given a time-stamp of when the backup occurred ("HistoryDate").
Here's the code for backing-up inventory:
DateTime historyDate = DateTime.Now;
MyDataContext db = new MyDataContext();
db.GetTable<InventoryHistory>().InsertAllOnSubmit(
db.GetTable<Inventory>()
.Select(i => new InventoryHistory
{
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
})
);
My questions are:
Should/Can this behavior be broken down into smaller unit-testable parts?
Since I am testing against a dedicated test database, should I be using a mocking tool and following the abstract factory pattern for any "repositories"?
The question I would ask is that is this really a unit test? A unit test would consider mocked Table<TEntity>
instances, because we're not concerned with the actual data, rather that the mechanism of creating the items is correct.
In your snippet above, it seems that you are unit testing the Linq methods themselves, not any specific code you have written yourself.
As for your last question, one of the fundamental mistakes made with mocking is the assumption of what to test when mocking. Typically you would be mocking a something consumed by the type you want to test. E.g.:
public ICalculatorService
{
int Add(int a, int b);
}
[Test]
public void CannAdd()
{
var mock = Mock<ICalculatorService();
mock.Setup(m => m.Add(It.IsAny<int>(), It.IsAny<int>()))
.Returns(100);
var service = mock.Object;
Assert(service.Add(1, 2) == 100); // Incorrect
}
The above is a pointless test, because I am testing that it is returning exactly what I have told it to. I'm not testing the Moq framework here, I need to test my code, so I would need to be testing the consumer:
public class Calculator
{
private readonly ICalculatorService _service;
public Calculator(ICalculatorService service)
{
_service = service;
}
public int Add(int a, int b)
{
return _service.Add(a, b);
}
}
[Test]
public void CannAdd()
{
var mock = Mock<ICalculatorService();
mock.Setup(m => m.Add(It.IsAny<int>(), It.IsAny<int>()))
.Returns(100);
var calculator = new Calculator(mock.Object);
Assert(calculator.Add(1, 2) == 100); // Correct
}
That's more like it (although a simplistic example). I am now testing the Calculator
consumer itself, not the consumable. In your example, even if you were mocking your DataContext to return dummy instances of Table<TEntity>
, what real benefits do you get?
Realistically you'd probably create a repository, e.g. an IInventoryRepository
, and create a consumer of that repository (could be a domain model, a controller, etc). Then through testing, you'd mock that repository, and test your consumer.
This looks like a fairly atomic operation to me, not much opportunity for breaking it apart.
Unit testing does not hit the database- that's integration testing. If you want a good unit test for this, you would test for behavior- in this case, that the history is backed up when it's supposed to be.
By way of full disclosure, I'm only just starting to learn EF and LINQ and the best way to test them, so you may get some more useful information regarding them in particular, so these are just general testing answers.
1.
I can't see a way that this can be further broken down to be isolated for unit testing, apart from:
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
being refactored into a seperate method to be unit tested as the only other code are LINQ calls, which MS are responsible for testing.
2.
I don't think you'd be able to introduce a seam to isolate it with the abstract repository factory pattern, since you're calling into a datacontext.
I'm not sure whether you should fake this (and since you'd be testing against it will be a mock proper - a fake that you test against, a fake that you don't test against is a stub) but since it's calling into a test server, you can make it an automated integration test since the functionality involves the interaction with the data store.
At first, method you describe looks simply and I'm not sure it needs any unit tests.
But, if you want to decompose it, you may do this:
Extract method for getting list of inventories for backup
IQueryable GetInventoryForBackup(this DataContext context)
{
return context.GetTable();
}
Extract method to convert Inventory to InventoryHistory
IEnumerable ToInventoryHistory(this IEnumerable data, DateTime historyDate)
{
return data.Select(i => new InventoryHistroy { ID = i.Id ....
}
Extract method to save sequence of InventoryHistory
void SaveHistory(IEnumerable data)
{
dataContext.InsertAllOnSubmit(data);
dataContext.SubmitChanges();
}
Now you have seemsful methods and you can easy write unit tests for.