How does one unit test a service which connects to a database?
I have a playerRepository class in data access layer, which interacts with the database directly, and a playerService class in business layer which creates an instance of playerRepository and services random stuff like - deleting player, saving player, getting all players, getting the player by id/name yadda yadda.
I want to unit test the playerService without using the real database, but using in-memory database provided with EF.
The problem is, I'm having trouble figuring out how to set it up.
I have a PlayerContext : DbContext class, which is used as a model to code-first (done this part via tutorial on EF). And I have to add a parameter to constructor DbContextOptionsBuilder<PlayerContext>, but I don't know where to begin. I don't know how and where to setup the connection string, where does the "default" database store itself.
I want to do this by using EF without NSubstitute or Moq, I'm doing this to learn how it's done without using other frameworks except for EF.
public class PlayerContext : DbContext
{
public PlayerContext() : base()
{
}
public DbSet<Player> Players { get; set; }
}
public class Player
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int DciNumber { get; set; }
}
I'm using visual studio provided unit testing
[TestClass]
public class PlayerServiceTest
{
// Should get all the players from database
[TestMethod]
public void GetAllPlayers()
{
// arrange - act
// TODO: Return all the players from the database
// assert
// TODO: Should return empty list
}
And the PlayerService class looks something like this
public class PlayerService
{
private PlayerRepository _playerRepository = new PlayerRepository();
public List<Player> GetAllPlayers()
{
var players = _playerRepository.GetAllPlayers();
return players;
}
PlayerRepository
public class PlayerRepository
{
public List<Player> GetAllPlayers()
{
using (var context = new PlayerContext())
{
var players = context.Players.ToList();
return players;
}
}
Generally my questions are:
How to make the PlayerContext with another connection string which connects to in-memory database in case of unit test, and also how to provide it with the correct connection string when not running via unit-tests
How to change the location of the database, because it's using a default path in C:\Users.
I'm not looking for integration tests with the DAL's PlayerRepository, I just want to test the business layer and all I need is, when I run the tests that the PlayerService to uses the PlayerRepository which connects to in-memory database and that's it. Otherwise it should connect to the local db stored in the same folder as the main exe for the app
Help needed!
The missing piece is dependency injection / IoC. The principle here is to define your dependencies (Repository) with a contract interface that can be mocked out. Inject that dependency into the classes that depend on it. This way you can substitute out concrete implementations like databases, file handling, etc. with mock up objects that return a known state, throw an expected exception, etc. to test your business logic's handling of those scenarios.
public class PlayerService
{
private readonly IPlayerRepository _playerRepository = null;
public PlayerService(IPlayerRepository playerRepository)
{
_playerRepository = playerRepository ?? throw new ArgumentNullException("playerRepository");
}
public List<Player> GetAllPlayers()
{
var players = _playerRepository.GetAllPlayers();
return players;
}
}
Have a look at IoC containers such as Autofac, Unity, Ninject, etc. for examples of how a container can be st up to automatically identify and inject concrete class instances into your services when it constructs them.
When you go to write a unit test you create a mock of the IPlayerRepository class (see: Moq for example) and pass that to your service under test. I.e.:
[Test]
public void TestService()
{
var mockRepository = new Mock<IPlayerRepository>();
mockRepository.Setup(x => x.GetPlayers()).Returns(buildTestPlayerList());
var serviceUnderTest = new PlayerService(mockRepository.Object);
// continue with test...
}
At a worst case: if you want to forgo the container, this can work as well:
public class PlayerService
{
private IPlayerRepository _playerRepository = null;
public IPlayerRepository PlayerRepository
{
get { return _playerRepository ?? (_playerRepository = new PlayerRepository()); }
set { _playerRepository = value; }
}
// ...
}
... and then with the test...
[Test]
public void TestService()
{
var mockRepository = new Mock<IPlayerRepository>();
mockRepository.Setup(x => x.GetPlayers()).Returns(buildTestPlayerList());
var serviceUnderTest = new PlayerService { PlayerRepository = mockRepository.Object };
// continue with test...
}
This is a pattern I call "lazy property injection" where you can opt to send a dependency in, but by default it will simply create the default dependency. This can be a useful pattern when introducing dependency substitution & unit testing into legacy code that was relying heavily on newing up classes mid-code. It's lazy in the sense that the dependency is only "newed" the first time it is accessed. If you call a method in the service that doesn't need the dependency then there is no initialization of every dependency, only the ones that are used. I highly recommend reading up on IoC containers though since they help automate wiring up dependencies.
Related
I'm new to unit testing and I'm really stuck atm so I could really use some help.
Some application info
I have a WPF application in MVVM. It gets data from a database (classes generated via .edmx).
All linq queries are handled by methods in the Database class.
In CustomerListViewModel, it makes a list of all Customers to be shown in the CustomerListView.
My problem
I am new to Unit Testing. I've read about it and tried to make it work. But as I understand, it should/can be done without touching the DB. I tried to find as much info as I could, but it wouldn't work with what I have. And now I'm basically stuck.
My question
How do I unit test this piece of code? How can I know if I've successfully queried the database (with or without touching the DB in the unit test)?
(If I understand it for this piece, I can figure the rest of the classes and methods out on my own)
The code
CustomerListViewModel:
public CustomerListViewModel()
{
MyObservableCollection<Customer> listCustomers = new MyObservableCollection<Customer>();
ListCustomers = App.Database.GetCustomerList();
}
private void GetListCustomers()
{
ListCustomers = App.Database.GetCustomerList();
if (App.Database.hasError)
App.Messenger.NotifyColleagues("SetStatus", App.Database.errorMessage);
}
Database:
public MyObservableCollection<Customer> GetCustomerList()
{
hasError = false;
MyObservableCollection<Customer> customerList = new MyObservableCollection<Customer>();
try
{
QRM_Entities dc = new QRM_Entities();
var query =
from customers in dc.Customer
select customers;
foreach (Customer cust in query)
{
customerList.Add(cust);
}
}
catch (Exception ex)
{
errorMessage = "GetCustomerList() error, " + ex.Message;
hasError = true;
}
return customerList;
}
The way that you have the ViewModel currently setup will make it almost impossible to unit test.
The issue is on this line:
ListCustomers = App.Database.GetCustomerList();
I presume that App is a static and Database is the class that you are using as your Data Access Layer. So everytime that you call the constructor of your CustomerListViewModel you will call the actual Static implementation of App which you would have to setup before creating the View Model, meaning that you would always be testing with the actual Database, which is obviously what you are attempting to bypass.
In comes my favorite software principle the Dependency Inversion Principle, the premise of this is that decouple modules so that your high level module depends on an abstraction of a lower level module. And that details should depend on that abstraction. Effectively you should develop to an interface and provide this interface to dependents.
Taking your example I would extract interfaces for your database interaction and provide these to your View Model, but I'll go a step further and provide this to a model which will be provided to your view model.
IDatabase:
public interface IDatabase
{
IEnumerable<ICustomer> GetCustomerList();
}
ICustomerListModel:
public interface ICustomerListModel
{
ObservableCollection<ICustomer> Customers
{
get;
}
}
CustomerListModel
public class CustomerListModel : ICustomerListModel
{
private readonly IDatabase database;
private readonly ObservableCollection<ICustomer> customers;
public CustomerListModel(IDatabase database)
{
this.database = database;
this.customers = new ObservableCollection(database.GetCustomerList());
}
public ObservableCollection<ICustomer> Customers
{
get
{
return this.customers;
}
}
}
CustomerListViewModel
public class CustomerListViewModel
{
private readonly ICustomerListModel customerListModel;
public CusomterListViewModel(ICustomerListModel customerListModel)
{
this.customerListModel = customerListModel;
}
public ObservableCollection<ICustomer> Customers
{
get
{
return this.customerListModel.Customers;
}
}
}
So what you can see here is that I have extracted an interface for the database which I request the information from, this means that I don't care about the implementation of the IDatabase, I just now that it provides me with a collection of ICustomer's when I call GetCustomerList().
So I inject a copy of the IDatabase into the CusomterListModel class which I can then query knowing that I'll get what I want back correctly. I then inject the ICustomerListModel into the ICustomerListViewModel so that the collection can be presented to the View.
So to test the CustomerListModel I would have a test like:
[Fact]
public void Customers_IsCorrectlyInitialisedAtStartup_Test()
{
var databaseMock = new Mock<IDatabse>();
var customer = new Customer();
var customers = new [] { customer };
databaseMock.Setup(mock => mock.GetCustomerList())
.Returns(customers);
var sut = new CustomerListModel(databaseMock.Object);
Assert.Equal(customers, sut.Customers);
}
In this I have mocked a version of the IDatabase, now you can see how I don't care in the version of CustomerListModel what IDatabase I have as long as I can call GetCustomerList(). This has a setup to return a ICustomer when a call to GetCustomerList() is called. Finally I am asserting that the Customers collection was correctly populated with the returns of the IDatabase call.
Unit testing is a fine art, difficult to understand at first but when you get it working at first you'll pick it up quickly. Some things you may wish to look at to help you with generating unit testable code and actually testing:
Solid Principles, principles that every Software Engineer should at least be familiar with, will help generating code that is testable.
Dependency Injection, the wiki-page on Dependency Injection outlining the pros and cons to injecting the dependency into a constructor, when and how to use it in further examples.
Moq, a friendly and easy to use mocking framework for C#, which I used as part of my example above.
I have a repository implemented using LINQ to SQL. I need to do Unit Testing though I don't have a database. How can I write the UT for FreezeAllAccountsForUser method? Can you please show an example using manually mocking?
Note: There is inheritance mapping used in domain objects
Note: Unit Testing is to be done using Visual Studio Team Test
Comment from #StuperUser. Unit testing involves completely isolating code from the other objects it interacts with. This means that if the code fails, you can be sure that the failure is to do with the code under test. To do this you have to fake these objects.
CODE
public void FreezeAllAccountsForUser(int userId)
{
List<DTOLayer.BankAccountDTOForStatus> bankAccountDTOList = new List<DTOLayer.BankAccountDTOForStatus>();
IEnumerable<DBML_Project.BankAccount> accounts = AccountRepository.GetAllAccountsForUser(userId);
foreach (DBML_Project.BankAccount acc in accounts)
{
string typeResult = Convert.ToString(acc.GetType());
string baseValue = Convert.ToString(typeof(DBML_Project.BankAccount));
if (String.Equals(typeResult, baseValue))
{
throw new Exception("Not correct derived type");
}
acc.Freeze();
DTOLayer.BankAccountDTOForStatus presentAccount = new DTOLayer.BankAccountDTOForStatus();
presentAccount.BankAccountID = acc.BankAccountID;
presentAccount.Status = acc.Status;
bankAccountDTOList.Add(presentAccount);
}
IEnumerable<System.Xml.Linq.XElement> el = bankAccountDTOList.Select(x =>
new System.Xml.Linq.XElement("BankAccountDTOForStatus",
new System.Xml.Linq.XElement("BankAccountID", x.BankAccountID),
new System.Xml.Linq.XElement("Status", x.Status)
));
System.Xml.Linq.XElement root = new System.Xml.Linq.XElement("root", el);
//AccountRepository.UpdateBankAccountUsingParseXML_SP(root);
AccountRepository.Update();
}
Repository Layer
namespace RepositoryLayer
{
public interface ILijosBankRepository
{
System.Data.Linq.DataContext Context { get; set; }
List<DBML_Project.BankAccount> GetAllAccountsForUser(int userID);
void Update();
}
public class LijosSimpleBankRepository : ILijosBankRepository
{
public System.Data.Linq.DataContext Context
{
get;
set;
}
public List<DBML_Project.BankAccount> GetAllAccountsForUser(int userID)
{
IQueryable<DBML_Project.BankAccount> queryResultEntities = Context.GetTable<DBML_Project.BankAccount>().Where(p => p.AccountOwnerID == userID);
return queryResultEntities.ToList();
}
public virtual void Update()
{
//Context.SubmitChanges();
}
}
}
Domain Classes
namespace DBML_Project
{
public partial class BankAccount
{
//Define the domain behaviors
public virtual void Freeze()
{
//Do nothing
}
}
public class FixedBankAccount : BankAccount
{
public override void Freeze()
{
this.Status = "FrozenFA";
}
}
public class SavingsBankAccount : BankAccount
{
public override void Freeze()
{
this.Status = "FrozenSB";
}
}
}
Auto generated Class by LINQ to SQL
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.BankAccount")]
[InheritanceMapping(Code = "Fixed", Type = typeof(FixedBankAccount), IsDefault = true)]
[InheritanceMapping(Code = "Savings", Type = typeof(SavingsBankAccount))]
public partial class BankAccount : INotifyPropertyChanging, INotifyPropertyChanged
Simply, you can't. The sole purpose of the repository implementation is talking to the database. So the database technology does matter and you should perform integration tests.
Unit testing this code is impossible because LINQ to Objects is a superset of LINQ to SQL. You might have a green unit test and still get the runtime exception when using real db because you used a feature of LINQ in your repository that cannot be translated into SQL.
The Repository responsibility is to persist domain objects and fetch them on request. i.e. it's job is to take an object and deserialize/serialize it to from some form of durable storage.
So tests for the repository have to test against the real storage in this case a DB. i.e. these are integration tests - tests that your class integrates with the external DB.
Once you have this nailed, the rest of the client/app doesn't have to work against the real DB. They can mock the repository and have fast unit tests. You can assume that GetAccount works since the integration tests pass.
More details:
By passing in the Repository object as a ctor or method arg, you open the doors for passing in a fake or a mock. Thus now the service tests can run without a real repository >> there is no DB-access >> fast tests.
public void FreezeAllAccountsForUser(int userId, ILijosBankRepository accountRepository)
{
// your code as before
}
test ()
{ var mockRepository = new Mock<ILijosBankRepository>();
var service = // create object containing FreezeAllAccounts...
service.FreezeAllAccounts(SOME_USER_ID, mockRepository);
mock.Verify(r => r.GetAllAccountsForUser(SOME_USER_ID);
mock.Verify(r => r.Update());
}
You can by using the IDbSet interface in your datacontext and extracting an interface for your datacontext class. Programming towards interfaces is the key to creating unit testable code.
The reason that you would want to create unit tests for these linq queries is to have a unit test for the logical query. Integration tests are subject to all kinds of false negatives. The db not being in the right state, other queries running at the same time, other integration tests, etc. It is very hard to isolate a db well enough for a reliable integration test. That is why integration tests are so often ignored. If i have to pick one, i want the unit test...
I'm researching NoSQL databases and have a question regarding unit testing. What is the appropriate method to unit test the business logic? How does one mock a NoSQL database?
Your business logic should not touch the database directly, but rather go through a database access layer. This lets you mock that intermediate layer for unit testing. To do this, you can use dependency injection and mocking. There are frameworks that can help you with both of these things, but you can also do it by hand. Here's an example:
Say we have a DAL:
public class DBDataProvider: IDataProvider
{
public string getData()
{
//SQL to get data from actual database.
}
}
As you can see, this implements an interface for something that provides data for your business layer. It might look something like this:
public Interface IDataProvider
{
String getData();
}
Your business layer might look something like this:
public BusinessClass
{
private IDataProvider dataProvider;
public BusinessClass()
{
dataProvider = new DBDataProvider();
}
public BusinessClass(IDataProvider provider)
{
dataProvider = provider;
}
public void doBusinessStuff()
{
dataProvider.getData();
//Do something with data.
}
}
So now in your production code, you will make your business class using the default constructor, which will automatically make your class with a connection to the DB. However, notice that we can create a BusinessClass with an IDataProvider that we specify. So, you can make a "fake" data provider just for testing:
public class MockDataProvider: IDataProvider
{
public string getData()
{
//return some expected result that you can control, without doing a DB call.
}
}
Now in your test, you can create a new MockDataProvider, and pass that into the constructor for BusinessClass. Your business class will now use your mock data provider, instead of the real DB.
Here I did everything by hand, but it gives you an idea of how this works. In real life, you can use mocking and dependency injection frameworks to write a bunch of that code for you.
The same way you mock any dependency. Write a nice, neat contract from which implementation details can be abstracted away, then mock that contract. Typically this is done by using the Data Access Layer as the contract(s).
Without getting into real implementation details, lets say you have a query in your method you want to test: (note, i copied this code from a ravenDB example, but i know 0 about ravenDB, so it might not even compile)
public void SomeMethod()
{
var name = "Hello";
var motto = "World";
using (var docStore = new DocumentStore("localhost", 8080).Initialize())
using (var session = documentStore.OpenSession()){
session.Store(new Company { Name = name, Motto = motto });;
session.SaveChanges();
}
}
That's going to be pretty hard to mock / test because it requires a db on localhost on 8080. Now, if you separate this logic out into another class:
public class AwesomeDAL
public virtual void AddCompany(string name, string motto){
using (var docStore = new DocumentStore("localhost", 8080).Initialize())
using (var session = documentStore.OpenSession()){
session.Store(new Company { Name = name, Motto = motto });;
session.SaveChanges();
}
}
and allow for the injection of the dependency (AwesomeDal):
public class ClassBeingTested
{
public AwesomeDal DAL { get; set; }
public ClassBeingTested() : this(new AwesomeDal()){}
public ClassBeingTested(AwesomeDal dal)
{
this.DAL = dal;
}
public void SomeMethod()
{
var name = "Hello";
var motto = "World";
this.DAL.AddCompany(name, motto);
}
}
And you can now test the BL code in isolation. Or you can simulate database exceptions, or anything else you need to test because your data access layer is abstracted, and its implementation is easily mockable with a framework like Moq or RhinoMocks
In addition to the already posted (correct) answers, let me propose an alternative solution: Use a real development database! Nothing is a more realistic mock that the real thing. If you test on it directly, at least you know that your code will actually run.
If you can abstract away your database easily, I recommend doing that, though.
I am testing the functionality of a StoreManager class which has a dependency on DataBaseConfiguration class.
public class StoreManager {
private DataBaseConfiguration dbConfig;
public void Store(string name) {
dbConfig.Store(name);
}
//other methods here
}
The StoreManager class stores to a database and the only way I can test if this method works fine is to query from the database. I have another class in production which does that..
public class QueryManager {
private DataBaseConfiguration dbConfig;
public string Query(QueryExpression expr) {
//query logic
string name = "somename";
return name;
}}
Eventhough I am concerned with testing only my StoreManager class it looks to me like I need to use the QueryManager class to test the storedvalues.
So I have a basic test case like this one...
[TestFixture]
public class StoreManagerTest {
[TestFixtureSetup]
public void Setup() {
DatabaseConfiguration dbConfig = new DatabaseConfiguration(/*test database details*/);
StoreManager sm = new StoreManager(dbConfig);
QueryManager qm = new QueryManager(dbConfig);
}
[Test]
public void TestStore_ValidStore() {
sm.Store("testname");
string queryResult = qm.Query(new QueryExpression("query_expr"));
Assert.AreSame(queryResult, "testname");
}}
As you can see, apart from the ClassUnderTest (which is StoreManager), the QueryManager class also has a dependency on DatabaseConfig.
I don't have a lot of logic inside the StoreManager class, it just delegates to the DataBaseConfig class to store (well actually there are some more classes involved in storing, its not the DataBaseConfig that actually stores the data.. but just for simplicity purpose, lets say so..)
I would like to know if there is a better way to handle this test without involving QueryManager at all?
Also is there a better way to inject the dependency on DataBaseConfiguration into the StoreManager class (considering that the DataBaseConfiguration class takes details about the connection string, etc., of the database to store the data into.. and I would like to pass in a test database rather than the production database connection string there).
To get the dependencies out of the way in testing the most common approach is to either use a handwritten stub or a mocking framework (i.e Moq or RhinoMocks).
Additionally you would have to enable users of the StoreManager class to pass in the DataBaseConfiguration dependency, otherwise you cannot stub it out w/o changing the code. Constructor injection as you do right now is common practice and a clean way to do this (this becomes more convenient if you use an IOC container when you have lots of dependencies), also see here.
If I understand you correctly, you just want to test that the StoreManager actually stores the value you pass to it, you are interested in the behavior of the StoreManager and its interactions with its dependency DataBaseConfiguration - right now you do that by querying the data store itself for verification.
Given that let's go through a bare-bones example using RhinoMocks - the only thing I changed was define the Store method in your DataBaseConfiguration class as virtual so RhinoMocks can override it.
//Arrange
string storeValue = "testname";
var dbConfigMock = MockRepository.GenerateMock<DataBaseConfiguration>();
dbConfigMock.Expect(x => x.Store(storeValue));
StoreManager sm = new StoreManager(dbConfigMock);
//Act
sm.Store(storeValue);
//Assert
dbConfigMock.AssertWasCalled(x => x.Store(storeValue));
This test verifies that the Store method was called on your DataBaseConfiguration class w/o any other dependencies - it tests the behavior of your StoreManager class. This test does not touch the DB nor does it affect any other classes.
Edit:
I'm not sure I understand the concern about using a mocking framework in production code - the mocking framework is only used in your test projects, no reference to it or any code changes are required to the production code itself.
Using a handwritten stub you can do the same assertions "manually": Define a test stub that stores how many times and with what value Store() was called (again this requires the Store method to be declared virtual so it can be overridden):
public class DataBaseConfigurationTest : DataBaseConfiguration
{
public int TimesCalled { get; set; }
public string LastNameStored { get; set; }
public DataBaseConfigurationTest()
{
TimesCalled = 0;
}
public override void Store(string name)
{
TimesCalled++;
LastNameStored = name;
}
}
Now use this test stub in your test instead of the "real" DataBaseConfiguration:
string storeValue = "testname";
DataBaseConfigurationTest dbConfigStub = new DataBaseConfigurationTest();
StoreManager sm = new StoreManager(dbConfigStub);
sm.Store(storeValue);
Assert.AreEqual(1, dbConfigStub.TimesCalled);
Assert.AreEqual(storeValue, dbConfigStub.LastNameStored);
I am tasked with pulling all the rows from a 3rd party vendor's SQLite data table, creating business objects from those records, and sending the new business objects off to another class.
Pseudo-code:
var databasePath = "%user profile%\application data\some3rdPartyVendor\vendor.sqlite"
var connection = OpenSqliteConnection(databasePath);
var allGizmoRecords = connection.Query(...);
var businessObjects = TransformIntoBizObjs(allGizmoRecords);
someOtherClass.HandleNewBizObjs(businessObjects);
I've got all that working.
My question is: How can I write this class so it's unit testable?
Should I:
use the repository pattern to mock out the data access
actually provide a dummy SQLite database in the unit test
Or any better ideas? I'm using C#, but this question seems rather language-agnostic.
You could inject a test-only Sqlite database quite easily, refactoring the code to look like below. But how are you asserting the results? The business objects are passed to someOtherClass. If you inject an ISomeOtherClass, that class's actions need to be visible too. It seems like a bit of pain.
public class KillerApp
{
private String databasePath;
private ISomeOtherClass someOtherClass;
public KillerApp(String databasePath, ISomeOtherClass someOtherClass)
{
this.databasePath = databasePath;
this.someOtherClass = someOtherClass;
}
public void DoThatThing()
{
var connection = OpenSqliteConnection(databasePath);
var allGizmoRecords = connection.Query(...);
var businessObjects = TransformIntoBizObjs(allGizmoRecords);
someOtherClass.HandleNewBizObjs(businessObjects);
}
}
[TestClass]
public class When_Doing_That_Thing
{
private const String DatabasePath = /* test path */;
private ISomeOtherClass someOtherClass = new SomeOtherClass();
private KillerApp app;
[TestInitialize]
public void TestInitialize()
{
app = new KillerApp(DatabasePath, someOtherClass);
}
[TestMethod]
public void Should_convert_all_gizmo_records_to_busn_objects()
{
app.DoThatThing();
Assert.AreEqual(someOtherClass.Results, /* however you're confirming */);
}
}
Using an IRepository would remove some of the code from this class, allowing you to mock the IRepository implementation, or fake one just for test.
public class KillerApp
{
private IRepository<BusinessObject> repository;
private ISomeOtherClass someOtherClass;
public KillerApp(IRepository<BusinessObject> repository, ISomeOtherClass someOtherClass)
{
this.repository = repository;
this.someOtherClass = someOtherClass;
}
public void DoThatThing()
{
BusinessObject[] entities = repository.FindAll();
someOtherClass.HandleNewBizObjs(entities);
}
}
[TestClass]
public class When_Doing_That_Thing
{
private const String DatabasePath = /* test path */;
private IRepository<BusinessObject> repository;
private ISomeOtherClass someOtherClass = new SomeOtherClass();
private KillerApp app;
[TestInitialize]
public void TestInitialize()
{
repository = new BusinessObjectRepository(DatabasePath);
app = new KillerApp(repository, someOtherClass);
}
[TestMethod]
public void Should_convert_all_gizmo_records_to_busn_objects()
{
app.DoThatThing();
Assert.AreEqual(someOtherClass.Results, /* however you're confirming */);
}
}
But this still feels quite cumbersome. There are two reasons, 1) the Repository pattern has been getting some bad press lately from Ayende, who knows a thing or two about Repository. And 2) what are you doing writing your own data access!? Use NHibernate and ActiveRecord!
[ActiveRecord] /* You define your database schema on the object using attributes */
public BusinessObject
{
[PrimaryKey]
public Int32 Id { get; set; }
[Property]
public String Data { get; set; }
/* more properties */
}
public class KillerApp
{
private ISomeOtherClass someOtherClass;
public KillerApp(ISomeOtherClass someOtherClass)
{
this.someOtherClass = someOtherClass;
}
public void DoThatThing()
{
BusinessObject[] entities = BusinessObject.FindAll() /* built-in ActiveRecord call! */
someOtherClass.HandleNewBizObjs(entities);
}
}
[TestClass]
public class When_Doing_That_Thing : ActiveRecordTest /* setup active record for testing */
{
private ISomeOtherClass someOtherClass = new SomeOtherClass();
private KillerApp app;
[TestInitialize]
public void TestInitialize()
{
app = new KillerApp(someOtherClass);
}
[TestMethod]
public void Should_convert_all_gizmo_records_to_busn_objects()
{
app.DoThatThing();
Assert.AreEqual(someOtherClass.Results, /* however you're confirming */);
}
}
The result is a much smaller class and a business object and data-layer that you can change more easily. And you don't even have to mock the database calls, you can configure and initialize ActiveRecord to use a test database (in-memory, even).
Well, the only thing that would really need to be tested here is TransformIntoBizObjs, I would think, since the connection code should have been written/tested elsewhere. Simply passing things that might show up to Transform and seeing if the right thing pops out would be what you need to do.
Remember to test all usecases of Transform, even potentially weird items that probably shouldn't end up in the function call, but might. Never know what people have been shoving in their databases.
Inversion of Control (IoC) and Dependency Injection (DI) will go a long way towards making your code more testable. There are a lot of frameworks out there that can assist you with this, but for your purposes you don't necessarily need to go to all that effort.
Start with extracting an interface that might look something like this:
Interface ISqlLiteConnection
{
public IList<GizmoRecord> Query(...);
}
Once you've done that, you should refactor the OpenSqlLiteConnection() method to return an instance of ISqlLiteConnection, rather than the concrete implementation. To test, just create a class that implements your interface, which mocks up the actual DB queries and connections with determinate results.
Databases are complicates, you need to test your query code and you need to test it against a real sqlite instance - otherwise you can't be sure you didn't hit some rare sqlite quirk or bug.
And since the only way to test your query is to run it on a real sqlite file, and it's really easy to include such a file with your test there's no point to adding another layer just to make it "more" testable or to have "pure" unit tests.
Just make sure to add all the strange edge cases you can think of to your sample file.