I'm looking at unit tests for the first time.
As I'm in Visual Studio 2008 I started with the build in testing framework.
I've hit the button and started looking at filling in the blanks, it all seems fairly simple.
Except, I can see two problems.
1) A lot of the blank unit tests seem to be redundant, is there a rule of thumb for choosing which methods not to write unit tests for.
2) Is there a best practise for writing tests for methods that read/write a database (SQL Server in this case)
I'll give an example for (1).
I'm writing unit tests for a WCF web service. We use wscf.blue to write our web service WSDL/XSD first.
Here's the path through the (heavily simplified) code for the methods which consumes a list of Users and writes them to the Users table in the database.
Entry Point
|
|
V
void PutOperators(PutOperatorsRequest request) (This method is auto generated code)
|
|
V
void PutOperatorsImplementation(PutOperatorsRequest input) (Creates a data context and a transaction, top level exception handling)
|
|
V
void PutEntities<T>(IEnumerable<T> input) (Generic method for putting a set of entities into the database, just a for loop, T is Operator in this case)
|
|
V
U PutEntity<T, U>(T entity) (Generic Method for converting the input to what the database expects and adding it to the DataContext ready for submission, T is Operator, U is the data layer entity, called User)
|
|
V
(This method calls 3 methods, first 2 of which are methods belonging to "entity" passed into this method, the 3rd is an abstract method that, when overridden, knows how to consume a BL entity and flatten it to a database row)
void EnsureIDPresent() (Ensures that incoming entity has a unique ID, or creates one)
void ValidateForInsert(AllOperators) (Does this ID already exists, etc)
User ToDataEntity(Operator entity) (simple field mapping excersice, User.Name = Operator.Name, etc)
So, as far as I can tell I have 3 methods that do something obviously testable:
EnsureIDPresent() - This method takes and input and modifies it in an easily testable way
ValidateForInsert() - This method takes the input and throws exceptions if criteria are not met
ToDataEntity() - This method takes an input, creates a data row entity and populates the values. Should be very easy to test.
There is also:
PutOperatorsImplementation() - It's here that DataContext.SubmitChanges() and TransactionScope.Complete() is called. Should I write tests to test what is written to the database? And then what? Delete them the records? Not sure what to do here.
I think I should delete the tests for:
PutOperators() - Auto generated code, one line, calls PutOperatorsImplementation()
PutEntities()- Just a for loop calling PutEntity(), and it's a generic method on a base class
PutEntity() - Calls three methods that already have unit tests and the calls DataContext.InsertOnSubmit.
I have a similar path for getting the data as well:
GetOperatorsResponse GetOperators(GetOperatorsRequest request) - Auto generated
GetOperatorsResponse GetOperatorsImplementation(GetOperatorsRequest input) - Set up DataContext
List<Operator> GetEntities() - Linq Query
Operator ToOperator(User) - Flattens one data entity into it's equivalent BL entity.
I think I should just be testing ToOperator() and GetEntities()
Should I just have a dedicated test database with known good test data in it?
Is that the correct way to approach this?
There is no "hard and fast" rule as to what you should and should not test.
Unit tests are there to Test any implementation your'e writing works and to enable you to be confident when re-factoring that you haven't broke anything. You need to consider whether the tests you write will give you value or not.
The main things you need to consider when deciding what code to cover are
How likely is the code I'm about to write / have written likely to
change and need refactoring?
Is the code written mainly custom code or autogenrated - If
autogenrated then there is little value in writing tests as your
simply just testing the autogenerator you are using does its job
properly (and you should be able to trust it).
You should not use databases or anything that can change outside of the test environment to test Data access code. Instead consider writing "Mocks" to mock the response from the data layer for your tests. This will ensure your tests are consistent. Consider looking at some mocking frameworks such as Rhino Mocks Or MOQ.
Always remember you are writing test for a reason and not for the sake of writing test. If you are not going to gain any value from the tests you write(e.g. if your codebase is not going to change) then simply don't write them.
Related
I am having trouble writing a FitNesse test for my application.
The test in itself is pretty simple.
We setup a loan, it has a few properies (Principal, Interest, Fees).
There is another object, Rules, which contains properties for paying back the loan.
The Loan class has a method called SplitLoan hat takes a Rules object, and will return a list of payments that need to be made.
A Payment would contain a total Amount, as well as individual amounts for Principal, Interest, and Fees.
How do I write this as a test in FitNesse?
I can't even get the call to happen because I don't know how I setup the Rules object that gets passed into the SplitLoan function.
I'm not positive this works in C#, but I know SLIM supports putting an object reference into a Symbol. You could use one fixture to build the Rules object, get a reference to it, then pass the symbol into another fixture that needs it as input.
http://fitnesse.org/FitNesse.SuiteAcceptanceTests.SuiteSlimTests.SlimSymbolCanHoldInstanceUsedAsParameter
Alternately, if you don't want to (or can't) get an object reference and store it in a symbol, you could have a fixture that builds the rules and stores them in a singleton with an identifier. Then you could pass that identifier in later fixtures and the fixture code could fetch the rules object from the singleton.
Does something like this do what you want? This is a fitSharp test.
|name|myrules|with|new|rules|
|with|myrules|
|set|myproperty|123|
|with|new|loan|
|set|principal|1000|
|set|interest|5|
|split loan|<<myrules|
|total|interest|principal|fees|
|100|80|5|15|
|100|78|7|15|
I have methods like this:
public static bool ChangeCaseEstimatedHours(int caseID, decimal? time)
{
Case c = Cases.Get(caseID);
if (c != null)
{
c.EstimatedHours = time;
return Cases.Update(c);
}
return false;
}
public static bool RemoveCase(int caseID)
{
return Cases.Remove(caseID);
}
which internally uses LINQ to do the queries.
I am wondering how I should go about testing these. They do not have a state so they are static. They also modify the database.
Thus, I would have to create a case, then remove it in the same test, but a unit test should only be doing 1 thing. What is usually done in these situations?
How do I test database queries, updates and deletes?
Thanks
A test that requires database access is probably the hardest test to maintain. If you can avoid touching the database in your unit test, I would try to create an interface for the Cases class.
interface ICase
{
ICase Get(int caseID);
bool RemoveCase(int caseID);
}
And then use RhinoMock, Moq to verify if the Get() and RemoveCase() were called.
If you insist you need to test with a database, then you will need to spend time on setting up a testing database and do proper data clean up.
There is a difference between "Knowing the path and walking the path". What you want to test would class as an integration test not a unit test. A unit test is something that is carried out in isolation without any external dependencies and therefore makes the test runnable even if the machine you are running it on does not have a physical database instance present on it. See more differences here.
That's why patterns like Repository are really popular when it comes to doing persistence based applications as they promote unit test-ability of the data layer via Mocking. See a sample here.
So, you have 2 options (the blue pill Or the red pill):
Blue Pill: Buy a tool like TypeMock Isolator essential or JustMock and you'll be able to mock anything in your code "and the story ends".
Red Pill: Refactor existing design of the data layer to one of the interface based patterns and use free Mocking frameworks like Moq or RhinoMocks and "see how deep the rabbit hole goes"
All the best.
Maybe you could consider approach based on Rails framework:
Initialize database tables content via presets (fixtures in Rails)
Open transaction
Run test
Rollback transaction
Go to point 2. with another test
I'm using Code First to map classes to an existing database. I need a way to unit test these mappings, which are a mix of convention-based, attribute-based, and fluent-api.
To unit test, I need to confirm that properties of the classes map to the correct table and column names in the database. This test needs to be performed against the context, and should cover all configuration options for code first.
At a very high level, I'd be looking to assert something like (pseudo-code):
Assert.IsTrue(context.TableFor<Widget>().IsNamed("tbl_Widget"));
Assert.IsTrue(context.ColumnFor<Widget>(w => w.Property).IsNamed("WidgetProperty"));
Another idea to consider is using Linq and ToString().
For eaxample this :
context.Widget.Select(c => c.Property).ToString()
Will result in this for SQL Server Provider :
"SELECT [Var_3].[WidgetProperty] AS [WidgetProperty] FROM [dbo].[Widget]..."
Now we could hide it all in some Extension method that and parses resulting SQL it would look almost like Your pseudo-code :
Assert.IsTrue(context.Widgets.GetSqlColumnNameFor(w => w.Property).IsNamed("WidgetProperty"));
Draft for extension :
public string GetSqlColumnNameFor<TSource>(this DbSet<T> source, Expression<Func<TSource, TResult>> selector)
{
var sql = source.Select(selector).ToString();
var columnName = sql... // TODO : Some regex parsing
return
columnName;
}
Similary we could create GetSqlTableNameFor().
UPDATE : I decided to look for some dedicates SQL Parsers, so this solution is more generic, obviously there is such a thing for .NET :
http://www.dpriver.com/blog/list-of-demos-illustrate-how-to-use-general-sql-parser/generate-internal-query-parse-tree-in-xml-for-further-processing/
The only way I can think of to cover every possible option would be to use the Entity Framework Power Tools to pre-compile the views of your DbContext, and probably use a combination of reflection on that generated type and RegEx on the generated code itself to verify everything maps the way you want it to. Sounds pretty painful to me.
Another thing that comes to mind is creating a facade around DbModelBuilder to intercept and check everything that passes through it, but I don't know if that would handle the convention-based stuff. Also sounds painful.
As a less-complete, but much easier alternative, you can probably knock out a large portion of this by switching to attribute-based mapping wherever possible. This would allow you to create a base test class, say, ModelTesting<TEntity>, which includes a few test methods that use reflection to verify that TEntity has:
A single TableAttribute.
Each property has a single ColumnAttribute or NotMappedAttribute.
At least one property with a KeyAttribute.
Each property type maps to a compatible database type.
You could even go so far as to enforce a naming convention based on the names of the properties and class (with a caveat for table-per-hierarchy types). It would also be possible to check the foreign key mappings as well. That's a write-once base class you can derive from once for each of your model types and catch the majority of your mistakes (well, it catches the majority of mine, anyway).
Anything that can't be represented by attributes, like TPH inheritance and such, becomes a little harder. An integration test that fires up the DbContext and does a FirstOrDefault on Set<TEntity>() would probably cover most of those bases, assuming your DbContext isn't generating your database for you.
If you wrote a method
public static string ToMappingString(this Widget obj)
Then you could easily testing this via approval tests ( www.approvaltests.com or nuget)
There's a video here: http://www.youtube.com/watch?v=vKLUycNLhgc
However, if you are looking to test "My objects save and retrive themselves"
Then this is a perfect place of "Theory Based Testing"
Theory based testing
Most unit test take the form of
Given A,B expect C
Theory based testing is
Given A,B expect Theory
The beauty of this is there is no need to worry about which particular form A & B take since you don't need to know C, so any random generator will work.
Example 1: Testing Add and Subtract methods
Normally you would have stuff like
Assert.AreEqual(5, Add(2,3));
Assert.AreEqual(9, Add(10,-1));
Assert.AreEqual(10, Add(5,5));
Assert.AreEqual(7, Subtract(10,3));
However if you wrote a Theory Test it would look like
for(int i = 1; i < 100; i++)
{
int a = random.Next();
int b = random.Next();
Assert.AreEqual(a, Subtract(Add(a,b),b, string.Format("Failed for [a,b] = [{0},{1}], a,b));
}
Now that you understand Theory based testing, the theory you are trying to test is
Given Model A
When A is stored to the database, and retrieved the resulting object is equal to A
What is the correct way to unit test a method that returns a sequence of complex objects?
I'm implementing a branch finder. I have a repository that returns a Branch object which has various information such as
Branch name
Address
Location
Products stocked
I have a repository that I've faked to always return a known sequence of Branch objects and I need to test the logic in my BranchFinder service.
The Find method takes a location (lat/long) and a product specification in the form of a Flags enum .
public IEnumerable<Branch> Find(Location location, Products products);
Knowing the data that the search is going to operate on, I can safely specify a location and product specification and know what to expect, but how do I assert that the sequence is correct.
e.g.
var loc = new Location(53.79424, -1.546112);
var results = service.Find(loc, Products.Milk | Products.Bananas);
I know for this given query that a certain subset of data should be returned in a certain order, and also that the distance from the current location to the branch should increase as you iterate through the result set.
What is the correct/best way to test these rules and situations?
First thing I would do would be to make sure you access the Service via an interface ILocatorService. Also make sure that any downstream dependency it has to get the Branch info also used via Interface (so that you can inject Mock later on in your test).
Also, be clear that you are testing ILocatorService itself (to make sure that any change you made in the service doest break it).
For Any Dependencies that ILocatorService uses, you can use RhynoMocks to return set of results (it might be going to DB or some other vendor app to give us list of branches)
That way your inputs to ILocatorService stays the same and the only thing you are testing is if any logic that you have changed is breaking existign test/functionality.
If you know how many results are retrieved from the Find method you could convert to a list or array and check each position to check if the Branch object has the correct values, that's how I'd do it.
I have similar methods in the business layer. I am new to unit testing and sometimes get confused. For an idea, can you suggest, what will be a better approach to test this method
behaviour? I am using C# NUnit and Moq
public int? AddNewCatRetID(string categoryName)
{
int? categoryID = 0;
Adapter.AddNewBlogCategoryReturnID(categoryName, ref categoryID);
if (categoryID.HasValue)
return categoryID;
return 0;
}
where
Adapter = Visual Studio 2008, Data Set Designer generated TableAdater
AddDeveloperCategoryReturnID() = Name of a function which utilises a Stored procedure in DB
It adds a new record, "Category" and returns its auto generated ID. If it is non zero, we take that result for further processing.
I know should not be interested in talking to Database, below is the procedure, just to give an idea about what is going on in DB
PROCEDURE [dbo].[AddDeveloperCategoryReturnID]
#NAME NVARCHAR(MAX),
#CATEGORY_ID INT OUTPUT
AS
BEGIN
INSERT INTO [AllTimeGreatProgrammersDateBase].dbo.CATEGORIES(NAME )
VALUES (#NAME );
SET #CATEGORY_ID = SCOPE_IDENTITY();
SELECT #CATEGORY_ID;
END
some issues
how to check the values returned using "ref" from the method
what will you prefer to test and not to test? will be great if can list
There are several options depending on the characteristics of the Adapter type. If AddDeveloperCategoryReturnID is virtual or an interface member, you can most likely use a Test Double (either a hand-rolled one or a dynamic mock) to replace its behavior with some test-specific behavior.
If is a non-virtual method, you have two options:
Refactor the method to make it more testable.
Write an automated test that involves a database round-trip.
Automated tests that involve the database are orders of magnitudes more difficult to write and maintain than pure unit tests, so I would tend to shoot for the refactoring option.
On the other hand, if you think that the stored procedure represents a valuable code asset that should be protected by an automated test, you have no recourse but to write the database test.
I'd first convert Adapter.AddNewBlogCategoryReturnID(categoryName, ref categoryID) so that instead of returning a variable by reference, it simply returned the value.
Then, I would extract that into a virtual method.
To test AddNewCatRetID, I would extend the class to make a testable version, and override that virtual method to return an int? stored in a public variable.
That way, when you test to see what happens when you call AddNewCatRetID in a situation where there's a 0 in the database, you don't need to actually put a 0 in the database - you just set that parameter on the testable version of your class, and when your test calls AddNewCatRetID, instead if hitting the database, it just returns the value you set. Your test is guaranteed to be faster if you can avoid hitting the database, and since it's MS's generated adapter, there's not really a need to test it - you only care about what your method does with what the adapter returns.