I am trying to do some unit tests and I need dummy data. Some of this data I need to have specific values, other will be just random.
I have for instance a "CreateProduct" method in my Service Layer and I am wondering if it is a good idea to use such methods instead of making a product by hand in my unit tests.
On the surface this seems like a good idea but I am worried maybe I will need to mocking or something to successfully get through that method.
The CreateProduct will try to save the product to the database but I have already a flag that will stop the save happening(used for unit of work scenario encase of rollbacks).
I am using EF 6-rc1 and mocking up the DataContext with moq and I was going to use AutoFixture but it does not work out of box for this secnario and I am starting to feel I am taking too much new tools on at once so maybe I should just do it manually for now.
It's hard to tell exactly what you are doing without a code example, but I sometimes use this implementation of an IDataSet that uses a List in memory. Typical usage would be something like:
using System.Data.Entity;
using System.Linq;
using Moq;
using NUnit.Framework;
namespace EFMock
{
internal interface IDataContext
{
IDbSet<DataItem> DataItems { get; set; }
}
class DataContext : DbContext, IDataContext
{
public IDbSet<DataItem> DataItems{ get; set; }
}
class DataItem
{
public int SomeNumber { get; set; }
public string SomeString { get; set; }
}
/* ----------- */
class DataUsage
{
public int DoSomething(IDataContext dataContext)
{
return dataContext.DataItems.Sum(x => x.SomeNumber);
}
}
/* ----------- */
[TestFixture]
class TestClass
{
[Test]
public void SomeTest()
{
var fakeDataItems = new [] {
new DataItem { SomeNumber = 1, SomeString = "One" },
new DataItem { SomeNumber = 2, SomeString = "Two" }};
var mockDataContext = new Mock<IDataContext>();
mockDataContext.SetupGet(x => x.DataItems).Returns(new FakeDbSet<DataItem>(fakeDataItems));
var dataUsage = new DataUsage();
var result = dataUsage.DoSomething(mockDataContext.Object);
Assert.AreEqual(2, result);
}
}
}
I also have a NuGet package named "FakeO" that you can use to create some fake objects, where some data is a specific value and some is random:
var fakeDataItems = FakeO.Create.Fake<DataItem>(10, // create an IEnumerable of 10 items
x => x.SomeNumber = FakeO.Number.Next(), // set to a random number
x => x.SomeString = "Always This String"); // set to a specific string
One thing to keep in mind with this kind of testing is that using an IQueryable against a List will use Linq2Objects and not Linq2Entities, so results of some Linq queries will be different.
If you want to Unit Test something, you'll just want to test the unit. If you use a method in the servicelayer to generate some fake data, the unit test is not only testing the unit under test, but also the method in the service layer.
So the answer to your question: no, it is not a good idea to use the service layer to dummy data
Related
Until today I had a hard time with unit testing. For this reason I just started to read a book "The art of Unit Testing".
The author states that each "unit of work" has entry and exit points and that there should be a unit test for each exit point.
An "exit point" could be:
A return value of a function (also an exception)
A state change (for example of a class property)
A third party system called (E-Mail service)
The entry point is usually a function call.
I was now eager to try this in one of my examples and I was successful. But for a price that I cannot accept. My tests are a huge amount of functions and I would like to get your opinion about them.
The test class I want to use is easy:
public class RoleAssignement
{
public string RoleId { get; }
public string EnterpriseId { get; }
public List<string> SiteIds { get; }
public RoleAssignement(string roleId, string enterpriseScopeId)
{
Ensure.ThrowIfNull(roleId);
Ensure.ThrowIfNull(enterpriseScopeId);
Ensure.ThrowIfIdNotValid(roleId);
Ensure.ThrowIfIdNotValid(enterpriseScopeId);
RoleId = roleId;
EnterpriseId = enterpriseScopeId;
}
public RoleAssignement(string roleId, List<string> siteScopeIds)
{
Ensure.ThrowIfNull(roleId);
Ensure.ThrowIfNull(siteScopeIds);
Ensure.ThrowIfIdNotValid(roleId);
foreach(var id in siteScopeIds)
{
Ensure.ThrowIfIdNotValid(id);
}
RoleId = roleId;
SiteIds = siteScopeIds;
}
}
You can see that I have just three properties. One of them (RoleId) must be set always. The other two parameters should be exclusively set (if one is null, the other must be set and vice versa).
In the language of the book I have two "entry points" - my two constructors.
But I have ten exit points that are:
1 If roleId is null for the first constructor, an exception should be thrown.
2 If roleId is null for the second constructor, an exception should be thrown.
3 If enterpriseScopeId is null for the first constructor, an exception should be thrown.
4 If siteScopeId is null for the second constructor, an exception should be thrown.
5, 6, 7, 8 If any of the four parameters have an invalid id, an exception should be thrown.
9 If the first constructor was called with the correct parameters, RoleId and EnterpriseId should be set but SiteIds should be null.
10 If the second constructor was called => vice versa.
If I write my unit tests now, I get a long list of testing functions - one of each exit point. I pasted this in the end of my question.
The thing is: Am I really on the right way here? I have a very easy test class now and the tests for it seem to explode in a huge jungle of tests.
Testing each possible exit point with its own function will slow me down in my coding and because the tests are testing such an easy behaviour they are also worthless for me.
Or are they?
But what will happen if I start to test the more complicated things? I will have a project with 1000 lines of production code and 10000 lines of tedious test functions.
I think I misunderstood something, but I don't know what I misunderstood. Or am I okay with my tests and I have to live with this from now?
Here is my testing code - all tests are passing. I even wrote them following TDD:
Testing code: ~100 lines
Production code: ~30 lines
Really?
using CP.Admin.Core.SDK.ValueObjects;
using DataHive.Validations.Exceptions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
namespace CP.Admin.Tests
{
[TestClass]
public class RoleAssignementTests
{
[TestMethod]
public void RoleAssignementFirst_NullRoleIdParameter_ThrowsArgumentNullException()
{
Action useConstructor = () =>
{
var roleAssignement = new RoleAssignement(null, "62500ac55988223c8b9b28fc");
};
Assert.ThrowsException<ArgumentNullException>(useConstructor);
}
[TestMethod]
public void RoleAssignementFirst_InvalidRoleIdParameter_ThrowsIdNotValidException()
{
Action useConstructor = () =>
{
var roleAssignement = new RoleAssignement("invalidId", "62500ac55988223c8b9b28fc");
};
Assert.ThrowsException<IdNotValidException>(useConstructor);
}
[TestMethod]
public void RoleAssignementFirst_NullEnterpriseScopeIdParameter_ThrowsArgumentNullException()
{
Action useConstructor = () =>
{
string param = null;
var roleAssignement = new RoleAssignement("62500ac55988223c8b9b28fc", param);
};
Assert.ThrowsException<ArgumentNullException>(useConstructor);
}
[TestMethod]
public void RoleAssignementFirst_InvalidEnterpriseScopeIdParameter_ThrowsArgumentNullException()
{
Action useConstructor = () =>
{
var roleAssignement = new RoleAssignement("62500ac55988223c8b9b28fc", "invalidId");
};
Assert.ThrowsException<IdNotValidException>(useConstructor);
}
[TestMethod]
public void RoleAssignementSecond_NullRoleIdParameter_ThrowsArgumentNullException()
{
Action useConstructor = () =>
{
var param = new List<string> { "62500ac55988223c8b9b28fc" };
var roleAssignement = new RoleAssignement(null, param);
};
Assert.ThrowsException<ArgumentNullException>(useConstructor);
}
[TestMethod]
public void RoleAssignementSecond_InvalidRoleIdParameter_ThrowsIdNotValidException()
{
Action useConstructor = () =>
{
var param = new List<string> { "62500ac55988223c8b9b28fc" };
var roleAssignement = new RoleAssignement("invalidId", param);
};
Assert.ThrowsException<IdNotValidException>(useConstructor);
}
[TestMethod]
public void RoleAssignementSecond_NullSiteScopeIdParameter_ThrowsArgumentNullException()
{
Action useConstructor = () =>
{
List<string> param = null;
var roleAssignement = new RoleAssignement("62500ac55988223c8b9b28fc", param);
};
Assert.ThrowsException<ArgumentNullException>(useConstructor);
}
[TestMethod]
public void RoleAssignementSecond_InvalidSiteScopeIdParameter_ThrowsIdNotValidException()
{
Action useConstructor = () =>
{
var param = new List<string> { "invalidId" };
var roleAssignement = new RoleAssignement("62500ac55988223c8b9b28fc", param);
};
Assert.ThrowsException<IdNotValidException>(useConstructor);
}
[TestMethod]
public void RoleAssignementFirst_ParametersAreOkay_AllValuesAreCorrect()
{
var roleAssignement = new RoleAssignement("62500ac55988223c8b9b28fc", "62500ac55988223c8b9b28fc");
Assert.IsNotNull(roleAssignement.RoleId);
Assert.IsNotNull(roleAssignement.EnterpriseId);
Assert.IsNull(roleAssignement.SiteIds);
}
[TestMethod]
public void RoleAssignementSecond_ParametersAreOkay_AllValuesAreCorrect()
{
var param = new List<string> { "62500ac55988223c8b9b28fc" };
var roleAssignement = new RoleAssignement("62500ac55988223c8b9b28fc", param);
Assert.IsNotNull(roleAssignement.RoleId);
Assert.IsNotNull(roleAssignement.SiteIds);
Assert.IsNull(roleAssignement.EnterpriseId);
}
}
}
As you recognize, going down this road will be very painful. Because wanting to assert for each possible case (every parameter value + every possible combination) will require (as you saw) more work than making the actual production code to work.
All of this because you are orienting tests regarding data.
If you consider testing the behavior of the system instead, you can break free from a lot of implementation details and focus on a higher level.
Considering behavior, the only one that I can eventually see is
The other two parameters should be exclusively set (if one is null, the other must be set and vice versa).
It corresponds to scenarii 9 and 10 according to your numerotation:
[TestMethod]
public void RoleAssignementFirst_ParametersAreOkay_AllValuesAreCorrect()
{
var roleAssignement = new RoleAssignement("62500ac55988223c8b9b28fc", "62500ac55988223c8b9b28fc");
Assert.IsNotNull(roleAssignement.RoleId);
Assert.IsNotNull(roleAssignement.EnterpriseId);
Assert.IsNull(roleAssignement.SiteIds);
}
[TestMethod]
public void RoleAssignementSecond_ParametersAreOkay_AllValuesAreCorrect()
{
var param = new List<string> { "62500ac55988223c8b9b28fc" };
var roleAssignement = new RoleAssignement("62500ac55988223c8b9b28fc", param);
Assert.IsNotNull(roleAssignement.RoleId);
Assert.IsNotNull(roleAssignement.SiteIds);
Assert.IsNull(roleAssignement.EnterpriseId);
}
Now the test codebase has a significant smaller proportion than before comparing to the production codebase, which is a better compromise because it tests the most important thing at a greatly reduced price (both in implementation and maintenance).
Going further
Let me allow you to see something that you may never has thought would be possible. RoleAssignment could require no tests and still enforce the same rules as the one you want by better using the type system.
Consider the following code:
public class RoleAssignement
{
public Id RoleId { get; }
public Either<Id, List<Id>> RelatedIds { get; }
public RoleAssignement(Id roleId, Either<Id, List<Id>> relatedIds)
{
RoleId = roleId;
RelatedIds = relatedIds;
}
}
I used a pattern called Value Object to get rid of primitive types. These value objects (Id and Either) encapsulate all the validation for a Id to be considered valid. When given to RoleAssignement constructor, you then know for sure that you are handling correct values. No more tests needed for RoleAssignement, the type system already enforce your constraints !
You can then extract tests from your scenarii to test only value object construction once. Which means that even if Id is used everywhere through the codebase, it requires only to test once.
I have two classes that implement an interface, but both classes have a parameter passed into the constructor to identify what class the application would need. I am trying to test one (GetAvailablity) method on (AvailablityRepoData) class when I create an instance of AvailablityRepoData I am getting an error for non-virtual method. I would really appreciate if someone can point me to the right direction.
public interface IAvailablityRepo
{
string GetAvailablity(Availablity availablity);
}
public class AvailablityRepoData: IAvailablityRepo
{
public AvailablityRepoData(string websetting) {
}
public string GetAvailablity(Availablity availablity) {
return "Data";
}
}
public class AvailablityRepoWeb:IAvailablityRepo
{
public AvailablityRepoWeb(string DataSetting) {
}
public string GetAvailablity(Availablity availablity) {
return "Web";
}
}
public class Availablity
{
public virtual string Id {
get;
set;
}
public virtual string Status {
get;
set;
}
}
var a = new Availablity() { Id = "111", Status = "A"};
Mock<IAvailablityRepo> mockRepo = new Mock<IAvailablityRepo>();
Mock<IAvailablityRepo> RepoData = new Mock<IAvailablityRepo>();
RepoData.Setup(x => x.GetAvailablity(It.IsAny<Availablity> ())).Returns("pass");
var result = RepoData.Object.GetAvailablity(a);
As has already been said in the comments, it's not clear from the code you've posted what your error is. If I copy and past it straight into visual studio (wrapping the test code in a test), the test passes fine. I'm going to suggest that when you experienced the error, you test code was actually closer to this:
[TestMethod]
public void TestMethod1() {
var a = new Availablity() { Id = "111", Status = "A" };
Mock<IAvailablityRepo> mockRepo = new Mock<IAvailablityRepo>();
Mock<AvailablityRepoData> RepoData = new Mock<AvailablityRepoData>();
RepoData.Setup(x => x.GetAvailablity(It.IsAny<Availablity>())).Returns("pass");
var result = RepoData.Object.GetAvailablity(a);
}
This results in an error when the test is run:
System.NotSupportedException:Invalid setup on a non-virtual (overridable in VB) member:
x => x.GetAvailablity(It.IsAny<Availablity>())
The difference between this test and your original test is that I've changed the Mocked type from the interface IAvailabilityRepo to AvailabilityRepoData which is the concrete class. Since Moq only supports mocking of interfaces / virtual methods it's naturally getting upset.
As has been mentioned by #prgmtc, your test as it stands doesn't really testing much of anything.
With your current code, it doesn't actually look like you need to be using Mocks at all. Something like this might be a more appropriate test:
[TestMethod]
public void TestDataRepoReturnsDataAvailability() {
var someImportantSetting = "thisShouldBeSomethingMeaningful";
var availability = new Availablity() { Id = "111", Status = "A" };
var sut = new AvailablityRepoData(someImportantSetting);
var returnedAvailability = sut.GetAvailablity(availability);
Assert.AreEqual("Data", returnedAvailability);
}
Assuming your actual code is more complex the string passed into your data repo would presumably need to be rather more meaningful...
As a general rule of thumb, you shouldn't be mocking the system under test. If you find yourself creating a mock for the system you're testing it's a good indication that you've got to much functionality in one class and/or you're trying to test the wrong thing...
As an asside, you may want to look into something like the builder pattern to create your different repos rather than passing the type into the constructor for each of the repos as you seem to be suggesting.
I need to some how unit test this method. The problem is that FsFileGroupFile is not easily mocked, it has complex constructor requirements and doesn't directly use an interface. _blockReaderFactory on the other hand is an interface and therefore easy to mock. How can I mock such a complicated object. I'm using Rhino Mocks and the Microsoft Unit Testing Framework. Any one have any ideas?
public void ReadGeneral(FsFileGroupFile a_file, FileItemData a_fileItemData)
{
try
{
var blockReader = _blockReaderFactory.Create(a_file.File.FullName, "CabinetData/StartData");
var version = blockReader.ReadVersion();
var name = blockReader.ReadString();
var type = blockReader.ReadString();
var defaultHeight = blockReader.ReadDouble();
var defaultWidth = blockReader.ReadDouble();
var defaultDepth = blockReader.ReadDouble();
a_fileItemData.Name = name;
a_fileItemData.DefaultWidth = defaultWidth * 100.0;
a_fileItemData.DefaultHeight = defaultHeight * 100.0;
a_fileItemData.DefaultDepth = defaultDepth * 100.0;
}
catch (Exception ex)
{
throw new IOException("General data could not be read from block data.", ex);
}
}
It seems that you're only using a_file to get the filename. So why not create an interface FilenameSupplier (or similar), and write a wrapper that implements it?
Java code example (added before question was tagged as C#...):
interface FilenameSupplier {
String getName();
}
public void ReadGeneral(FilenameSupplier a_file, FileItemData a_fileItemData) {
...
a_file.getName();
...
}
class ConcreteSupplier implements FilenameSupplier {
private final FsFileGroupFile file;
public ConcreteSupplier(FsFileGroupFile file) { this.file = file; }
String getName() { return a_file.File.FullName; }
}
You should extract some interface from FsFileGroupFile and pass it into constructor argument.
Then you can easily mock this interface with your preferable framework, Rhino Mocks in your case.
If it is not appropriate, you should build your FsFileGroupFile and may be use mocks when passing arguments in its complex constructor.
Seems that there is no another options, except may be you should review your design here. If classes are so hard to test it can be a sign of poor design.
When I have had to create complicated objects in a test I've used the Test Data Builder Pattern. As an example let's assume that you have five values to pass to the constructor:
public FsFileGroupFile(string firstProperty, string secondProperty,
string thirdProperty, string fourthProperty, string fifthProperty)
{
// constructor logic goes here
}
This would then be wrapped with a test builder class in the unit test project:
public class FsFileGroupFileBuilder
{
public string FirstProperty { get; set; }
public string SecondProperty { get; set; }
public string ThirdProperty { get; set; }
public string FourthProperty { get; set; }
public string FifthProperty { get; set; }
public FsFileGroupFile Build()
{
return new FsFileGroupFile(FirstProperty, SecondProperty, ThirdProperty,
FourthProperty, FifthProperty);
}
}
Now you can assign values to only the properties you care about and build your object in this way:
// in your test setup use this to initial to a default/valid state
var fsFileGroupBuilder = new fsFileGroupBuilder
{
FirstProperty = "Default",
SecondProperty = "Default",
ThirdProperty = "Default",
FourthProperty = "Default",
FifthProperty = "Default"
}
Note: Rhino Mocks can probably set those default value for you, but I have not used it personally so I'm not sure.
// Override the properties in each test
fsFileGroupBuilder.ThirdProperty = "Value needed for unit test."
// create
var fileItemData = new FileItemData();
ReadGeneral(fsFileGroupBuilder.Build(), fileItemData);
There are other open source libraries that can help with generating the test data such as NBuilder which have worked well for me in the past.
The main point here is that the complex constructor can be abstracted away with a builder which will allow you to concentrate on testing your business logic instead of satisfying the constructor in every test.
I am new to using Mocks. But what are it's main purposes? I'm going to start by using Moq to test my application (and NUnit).
For example, I have code that does things like this:
My webpage code behind:
public partial class MyWebpage
{
protected string GetTitle(string myVar)
{
return dataLayer.GetTitle(myVar);
}
}
My data access layer:
public class DataLayer
{
public string GetTitle(string myVar)
{
// Create the query we want
string query = "SELECT title FROM MyTable " +
"WHERE var = #myVar";
//ENTER PARAMETERS IN HERE
// Now return the result to the view
return this.dataProvider.ExecuteMySelectQuery(
dr =>
{
//DELEGATE DATA READER PASSED IN AND TITLE GETS RETURNED
},
query,
parameters);
}
}
My data provider talks and interacts directly with the db:
public class DataProvider
{
public T ExecuteMySelectQuery<T>(Func<IDataReader, T> getMyResult, string selectQuery, Dictionary parameters)
{
//RUNS AND RETURNS THE QUERY
}
}
What's the best way to test all of this?
If you want to test the layers separately, you would need to create interfaces for your DataProvider and DataLayer classes that expose the methods that you want to Mock. Then you can use a mocking framework - NSubstitute is very good, less code to write to create the mocks - to mock out the calls to the dependent classes, leaving you to test the code within that specific unit
public interface IDataProvider
{
T ExecuteMySelectQuery<T>(Func<IDataReader, T> getMyResult, string selectQuery, Dictionary parameters);
}
public interface IDataLayer
{
string GetTitle(string myVar);
}
public class DataLayer
{
private IDataProvider dataProvider;
public DataLayer(IDataProvider dataProvider)
{
this.dataProvider = dataProvider;
}
}
Then, in your test code, you create mocks instead of real objects and pass those into the constructor when you instantiate your test objects. To test the DataLayer:
[Test]
public void WhenRetievingTitleFromDataStore_ThenDataLayerReturnsTitle()
{
var title = "Title";
var dataProviderMock = new Mock<IDataProvider>(MockBehavior.Strict);
dataProviderMock.Setup(x => x.ExecuteMySelectQuery(<parameters>)).Returns(title);
var dataLayer = new DataLayer(dataProviderMock.Object);
Assert.That(dataLayer.GetTitle(It.IsAny<string>(), Is.EqualTo(title));
}
The only thing that can go wrong with that is the DB call (the query or the returned result is of wrong data types). That can't be mocked. You need to do integration tests and not unit tests.
Typically you only mock to be able to test logic in the code. You should for instance test so that the data mapper (this.dataProvider.ExecuteMySelectQuery) works as defined. but that's of the scope of the code in question.
Update
So you got the following classes:
public class DataLayer
{
public string GetTitle(string myVar)
{
// Create the query we want
string query = "SELECT title FROM MyTable " +
"WHERE var = #myVar";
//ENTER PARAMETERS IN HERE
// Now return the result to the view
return this.dataProvider.ExecuteMySelectQuery(
dr =>
{
//DELEGATE DATA READER PASSED IN AND TITLE GETS RETURNED
},
query,
parameters);
}
}
public class DataProvider
{
public T ExecuteMySelectQuery<T>(Func<IDataReader, T> getMyResult, string selectQuery, Dictionary parameters)
{
//RUNS AND RETURNS THE QUERY
}
}
If we examine the ExecuteMySelectQuery we can see that the DataLayer class is dependent of how the types that the database returns since the DataProvider just ease the query execution. One could say that it's an addon on top of ADO.NET.
That also means that you can never guarantee that DataLayer return what's promised without involving the database. Let's for instance assume that the table in the database has a column called title but someone managed to use the int data type instead.
The things that can go wrong are
The query is incorrect
The schema in the database is incorrect (wrong column names, data types etc)
The mapping
None of those errors can be detected nor tested with the help of a mock.
If you on the other hand use the DataLayer clas in another class you can of course mock it. Because the DataLayer class itself is a complete abstraction. That means that the callers of the class doesn't have to be aware of anything beneath it. Hence mocking is perfectly fine.
This post is meant to be more a discussion-starter, as I am somewhat new to unit testing and TDD.
I am currently writing some unit tests for a .NET process that interacts with several databases, and am using mock database contexts in an attempt to cover different edge cases within my tests, verify exception handling in the program itself, among other things. That being said, some of my unit tests use valid data, while others do not.
I am looking for feedback in terms of suggested best practices when adding valid/fake data to your mock database contexts. I've seen people do this a number of ways (e.g. - implement repository pattern, adding mock data to .csv files and making them part of the project, etc...).
I'm currently thinking about using a repository pattern for adding Survey objects to the Surveys table in my target DB.
First off, I've got the interface:
public interface ISurveyRepository
{
IQueryable<Survey> SurveySeries { get; }
}
This is implemented both for the mocking fake/valid data repositories as needed by unit tests
class FakeSurveyRepository : ISurveyRepository
{
private static IQueryable<Survey> fakeSurveySeries = new List<Survey> {
new Survey { id = 1, SurveyName="NotValid1", SurveyData="<data>fake</data>"},
new Survey { id = 2, SurveyName="NotValid2", SurveyData="<data>super fake</data>"},
.........,
new Survey {id = 10, SurveyName="NotValid10", SurveyData="<data>the fakest</data>" }
}.AsQueryable();
public IQueryable<Survey> SurveySeries
{
get { return fakeSurveySeries; }
}
}
// RealSurveyRepository : ISurveyRepository is similar to this, but with "good" data
I then have a class to consume this data for either fake/valid data by being passed a reference to the series in the constructor:
public class SurveySeriesProcessor
{
private ISurveyRepository surveyRepository;
public SurveySeriesProcessor( ISurveyRepository surveyRepository )
{
this.surveyRepository = surveyRepository;
}
public IQueryable<Survey> GetSurveys()
{
return surveyRepository.SurveySeries
}
}
And can then approach using these objects in my tests such as:
[TestClass]
public class SurveyTests
{
[TestMethod]
WhenInvalidSurveysFound_SurveyCopierThrowsInvalidSurveyDataErrorForEach()
{
// create mocking DB context and add fake data
var contextFactory = new ContextFactory( ContextType.Mocking );
var surveySeriesProcessor = new SurveySeriesProcessor( new FakeSurveyRepository() );
foreach(Survey surveyRecord in surveySeriesProcessor.GetSurveys() )
{
contextFactory.TargetDBContext.Surveys.AddObject( surveyRecord );
}
// instantiate object being tested and run it against fake test data
var testSurveyCopier = new SurveyCopier( contextFactory );
testSurveyCopier.Start();
// test behavior
List<ErrorMessage> errors = testSurveyCopier.ErrorMessages;
errors.Count.ShouldEqual( surveySeriesProcessor.GetSurveys().Count );
foreach(ErrorMessage errMsg in errors)
{
errMsg.ErrorCode.ShouldEqual(ErrorMessage.ErrorMessageCode.InvalidSurveyData);
}
}
}
NOTE: I realize that in the example code provided I don't necessarily need to make the classes implementing ISurveyRepository return the series as an IQueryable<Survey> (they could very well be List<Survey>). However, I am going to extend the functionality of the interface and these classes in the future to filter out the fake/valid series based on certain criteria added to LINQ queries, which is why I made the repositories implement IQueryable<>. This is mock-up code designed to convey the basic principles of what I'm thinking.
With all of this in mind, what I'm asking is:
Do you have any suggestions in terms of alternative approaches I could take in such scenarios?
What methods have you employed in the past, what did you like/not like about them? Which have you found were the easiest to maintain?
Given what I've posted, do you notice flaws in my general approach to unit testing? Sometimes I feel as though I write unit tests that attempt to cover too much ground instead of being concise, elegant, and to-the-point.
This is meant to be somewhat of an open discussion. Please keep in mind, this is the first set of unit tests I've ever written (I've read a decent amount of literature on the subject, however).
I think you're on a good track.
Personally, in the same situation, if I were dealing with a repository style pattern,
public interface IRepository<T>
{
IEnumerable<T> GetAll();
}
public class PonyRepository : IRepository<Pony>
{
IEnumerable<Pony> GetAll();
}
To actually supply me the data I need, I generally create a TestObjects or TestFakes class to supply the required data on-demand.
public class FakeStuff
{
public static IEnumerable<Pony> JustSomeGenericPonies(int numberOfPonies)
{
// return just some basic list
return new List<Pony>{new Pony{Colour = "Brown", Awesomeness = AwesomenessLevel.Max}};
// or could equally just go bananas in here and do stuff like...
var lOfP = new List<Pony>();
for(int i = 0; i < numberOfPonies; i++)
{
var p = new Pony();
if(i % 2 == 0)
{
p.Colour = "Gray";
}
else
{
p.Colour = "Orange";
}
lOfP.Add(p);
}
return lOfP;
}
}
And test with this as such:
[Test]
public void Hello_I_Want_to_test_ponies()
{
Mock<IRepository<Pony> _mockPonyRepo = new Mock<IRepository<Pony>>();
_mockPonyRepo.SetUp(m => m.GetAll()).Returns(FakeStuff.JustSomeGenericPonies(50));
// Do things that test using the repository
}
So this delivers reusability of the fake data, by keeping it out of the repository and in a place of it's own, meaning I can call this list of ponies anywhere a test requires a list of ponies, not just where a repository is involved.
If I need specific data for a specific testcase, i'll implement something like you had, but be a bit more explicit about what that particular Fake repository is for:
public class FakePonyRepositoryThatOnlyReturnsBrownPonies : IRepository<Pony>
{
private List<Pony> _verySpecificAndNotReusableListOfOnlyBrownPonies = new List....
public IEnumerable<Pony> GetAll()
{
return _verySpecificAndNotReusableListOfOnlyBrownPonies;
}
}
public class FakePonyRepositoryThatThrowsExceptionFromGetAll : IRepository<Pony>
{
public IEnumerable<Pony> GetAll()
{
throw new OmgNoPoniesException();
}
}
You mentioned CSV files as well - this could be viable (have used XML in the past), but I'd argue that holding fake data in a CSV or XML is just a worse version of keeping data in a localised DB using SQL CE or some equivalent. However, both of those are less maintainable and, crucially, in terms of unit tests, slower than using in-memory fake objects. I personally wouldn't use a file-based approach anymore unless I was specifically testing serialization or IO or something.
Hope there's something useful among all that lot...