patterncsharpMinor
Testing an abstract class
Viewed 0 times
abstracttestingclass
Problem
I started writing a set of tests for my
```
[TestClass]
public class SageContextBaseOpenTests
{
private Mock GetMockView(string viewId)
{
var view = new Mock();
view.SetupAllProperties();
view.SetupGet(m => m.ViewId).Returns(viewId);
view.Setup(m => m.Compose(It.IsAny()));
view.Setup(m => m.Dispose());
view.Setup(m => m.Insert());
view.Setup(m => m.Delete());
view.Setup(m => m.Update());
view.Setup(m => m.FilterCount(It.IsAny()));
return view;
}
private Mock GetMockSession()
{
var dbLink = new Mock();
dbLink.SetupAllProperties();
const string headersViewId = "PO0620";
var headersView = GetMockView(headersViewId);
dbLink.Setup(m => m.OpenView(headersViewId)).Returns(headersView.Object);
const string detailsViewId = "PO0630";
var detailsView = GetMockView(detailsViewId);
dbLink.Setup(m => m.OpenView(detailsViewId)).Returns(detailsView.Object);
var session = new Mock();
session.SetupAllProperties();
session.Setup(m => m.OpenDbLink(It.IsAny(), It.IsAny())).Returns(dbLink.Object);
return session;
}
[TestMethod]
public void Open_InitializesSessionWithSpecifiedAppInfo()
{
// arrange
var credentials = new SageCredential("testuser", "testpwd", "testdb");
var appInfo = new SageAppInfo("testapp", "v42");
var session = GetMockSession();
var context = new TestSageContext(credentials, appInfo, session.Object);
// act
context.Open();
// assert
session.Verify(m => m.Init(It.IsAny(), appInfo)
SageContextBase class, which needs to be abstract because the client code must derive from it (think of it as EF's DbContext - it can't be instantiated directly, but it packs a truckload of functionality) - I have 5 passing tests covering everything that should be happening inside the Open method:```
[TestClass]
public class SageContextBaseOpenTests
{
private Mock GetMockView(string viewId)
{
var view = new Mock();
view.SetupAllProperties();
view.SetupGet(m => m.ViewId).Returns(viewId);
view.Setup(m => m.Compose(It.IsAny()));
view.Setup(m => m.Dispose());
view.Setup(m => m.Insert());
view.Setup(m => m.Delete());
view.Setup(m => m.Update());
view.Setup(m => m.FilterCount(It.IsAny()));
return view;
}
private Mock GetMockSession()
{
var dbLink = new Mock();
dbLink.SetupAllProperties();
const string headersViewId = "PO0620";
var headersView = GetMockView(headersViewId);
dbLink.Setup(m => m.OpenView(headersViewId)).Returns(headersView.Object);
const string detailsViewId = "PO0630";
var detailsView = GetMockView(detailsViewId);
dbLink.Setup(m => m.OpenView(detailsViewId)).Returns(detailsView.Object);
var session = new Mock();
session.SetupAllProperties();
session.Setup(m => m.OpenDbLink(It.IsAny(), It.IsAny())).Returns(dbLink.Object);
return session;
}
[TestMethod]
public void Open_InitializesSessionWithSpecifiedAppInfo()
{
// arrange
var credentials = new SageCredential("testuser", "testpwd", "testdb");
var appInfo = new SageAppInfo("testapp", "v42");
var session = GetMockSession();
var context = new TestSageContext(credentials, appInfo, session.Object);
// act
context.Open();
// assert
session.Verify(m => m.Init(It.IsAny(), appInfo)
Solution
Just a couple of things.
You repeat this set up in every test.
Extract the fixtures into fields and create a
The
It could be perfectly ok, but always worth a second look. It's a balancing act between making sure the tests go red if a breaking change is made, and not testing the implementation.
In a perfect world, we'd never have to
You repeat this set up in every test.
var credentials = new SageCredential("testuser", "testpwd", "testdb");
var appInfo = new SageAppInfo("testapp", "v42");Extract the fixtures into fields and create a
[TestInitialize] routine. If you need to change them later, then you have a different fixture and should create a new test class anyway. It helps to keep your test classes small, focused, and clean. The
It.IsAny() method is useful for getting your test to initially pass, but I get hesitant when I see it sticking around in code that's "done". I get doubly hesitant when I see it in a Verify(). session.Verify(m => m.Init(It.IsAny(), appInfo));It could be perfectly ok, but always worth a second look. It's a balancing act between making sure the tests go red if a breaking change is made, and not testing the implementation.
In a perfect world, we'd never have to
Verify() anything. We would just Assert the output. We don't live in a perfect world, so this all might be fine, but it's definitely worth reflecting on.Code Snippets
var credentials = new SageCredential("testuser", "testpwd", "testdb");
var appInfo = new SageAppInfo("testapp", "v42");session.Verify(m => m.Init(It.IsAny<string>(), appInfo));Context
StackExchange Code Review Q#121492, answer score: 2
Revisions (0)
No revisions yet.