I have used Constructor injection on a repository for class, I have noticed the following works:
public CreateInvoiceResult CreateInvoice(string Code, int qty, string Name)
{
if (string.IsNullOrEmpty(Code) || qty <= 0 || repository.GetByName(Name).ID <= 0)
{
return new CreateInvoiceResult(false);
}
However changing to the following code (adding in 'Customer cust') causes test to fail?
public CreateInvoiceResult CreateInvoice(string stockCode, int quantity, string customerName)
{
Customer cust = repository.GetByName(Name);
if (string.IsNullOrEmpty(Code) || qty <= 0 || cust.ID <= 0)
{
return new CreateInvoiceResult(false);
}
Example Test:
Please can you explain why this is happening and how I can correct?
EDIT: Have updated test using Moq, to use correct repository:
[TestClass]
public class MockCustomerRepositoryDBTests
{
public MockCustomerRepositoryDBTests()
{
IList<Customer> customers = new List<Customer>
{
new Customer { ID = 1, Name = "Jim Smith",
Address = "14 Main Road"},
new Customer { ID = 2, Name = "Alex Smith",
Address = "78 Avanue"},
new Customer { ID = 3, Name = "Paul Brown",
Address = "1 Main Road"}
};
// Mock the CustomerRepositoryDB Repository using Moq
Mock<ICustomerRepository> mockCustomerRepository = new Mock<ICustomerRepository>();
// Return a customer by Name
mockCustomerRepository.Setup(mr => mr.GetByName(
It.IsAny<string>())).Returns((string s) => customers.Where(
x => x.Name == s).Single());
// Complete the setup of the Mock Customer Repository
this.MockCustomerRepository = mockCustomerRepository.Object;
}
public readonly ICustomerRepository MockCustomerRepository;
[TestMethod]
public void stockCodeIsNullOrEmpty()
{
//Arrange
var x = new InvoiceController(MockCustomerRepository);
//Act
bool result = x.CreateInvoice("", 1, "test").Success;
//Assert
Assert.AreEqual(result, false);
}
Getting 'System.InvalidOperationException: Sequence contains no elements'
your call to
Customer _Customer = repository.GetByName(customerName);
is probably failing with some exception.
If you look at your class you only initialize you repository object in this constructor:
public PartInvoiceController(ICustomerRepository custRepo)
{
this.repository = custRepo;
}
however you provide a default constructor which you call in the test:
//Arrange
var x = new PartInvoiceController();
So your repository is never initialised and so you will get a null reference exception.
Why didn't it fail before?
When it was part of the if statement it didn't matter as it was never executed, because the string.IsNullOrEmpty(stockCode) is true and the other conditions are combined using the conditional-or operator (||), which will short-circuit evaluating conditions if it can tell that the condition is going to be true or false even if all conditions are not evaluated.
The if statement was entered because of this and the other conditions were never evaluated, so the repository being null was code that was never executed.
To correct it you need to either provide a repository to use in the test (a stub or a mock), or create a default repository in the default constructor, or revert to the original code (as long as you don't care about the repository not being initialised in this test).
change your test like this:
[TestMethod]
public void stockCodeIsNullOrEmpty()
{
ICustomerRepository testRepository = //create a test repository here
//Arrange
var x = new PartInvoiceController(testRespository);
//Act
bool result = x.CreatePartInvoice("", 1, "test").Success;
//Assert
Assert.AreEqual(result, false);
}
EDIT
really you should ask another question about your mock problems, but basically you are looking for a user with the name "test" but none of your users in the mock repository have that name
Customer _Customer = repository.GetByName(customerName);
Member variable "repository" is not initialized before using. You should use the other constructor of class PartInvoiceController.
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.
Background
I'm fixing unit tests which have been neglected for a long time for legacy code in our organisation. They're written using Rhino Mocks 3.4.0, and I'm struggling to find a way of making this test pass. Rhino Mocks documentation seems to have gone, and most answers here and blogs seem to be using updated 3.5 and 3.6 syntax.
I'm wary of updating the version of Rhino Mocks we're using, as we have several thousand unit tests which may or may not need updated if we update.
The scenario:
We have a Presenter and a View. When the Presenter is initialised, it sets some default filter properties in the View. In the past, both of these properties were enums and the test passed.
The last change updated one of the properties to be an instance of a class. The test was updated to expect a call to a static method which creates an instance with default values (matching the code under test), but the test now fails with the error Rhino.Mocks.Exceptions.ExpectationViolationException : Unordered method call.
Some sample code:
public enum FilterOptions { OptionA, OptionB, OptionC }
public class OtherFilterOptions
{
public bool Filter1 { get; set;}
public bool Filter2 { get; set; }
public OtherFilterOptions(bool filter1 = true, bool filter2 = false)
{
Filter1 = filter1;
Filter2 = filter2;
}
public static OtherFilterOptions DefaultFilterOptions()
{
return new OtherFilterOptions();
}
}
public interface IToTestView
{
FilterOptions Property1 { set; }
OtherFilterOptions Property2 { set; }
}
public class ToTestPresenter
{
public IToTestView View { get; set; }
public ToTestPresenter(IToTestView view)
{
View = view;
}
public void InitialiseView()
{
View.Property1 = FilterOptions.OptionA;
View.Property2 = OtherFilterOptions.DefaultFilterOptions();
}
}
And a failing test:
[TestFixture]
class Tests
{
[Test]
public void TestOne()
{
var mocks = new MockRepository();
var mockView = mocks.CreateMock<IToTestView>();
ToTestPresenter presenter = new ToTestPresenter(mockView);
using (mocks.Ordered())
{
mockView.Property1 = FilterOptions.OptionA;
mockView.Property2 = OtherFilterOptions.DefaultFilterOptions();
}
mocks.ReplayAll();
presenter.InitialiseView();
mocks.VerifyAll();
}
}
The full error is
Rhino.Mocks.Exceptions.ExpectationViolationException : Unordered method call! The expected call is: 'Ordered: { IToTestView.set_Property2(RhinoMocksTestApp.OtherFilterOptions); }' but was: 'IToTestView.set_Property2(RhinoMocksTestApp.OtherFilterOptions);'
I'm assuming that the test is failing because the value to be set is a method call rather than a concrete value. I've tried declaring a variable using mockView.Property2 = theVariable, but there's no change to the error.
Can I set an expectation that Property2 will be set to {some object with Values Filter1 = true, Filter2 = false}? I've seen examples doing similarly using Rhino Mocks 3.6, but is anything available using 3.4.0?
Edit:
As an example, this is an example test which passes in Rhino Mocks 3.6.1 - I'm hoping to find some syntax that works similarly for 3.4.0, if it exists.
[Test]
public void TestOne()
{
var mocks = new MockRepository();
var mockView = MockRepository.GenerateMock<IToTestView>();
ToTestPresenter presenter = new ToTestPresenter(mockView);
mocks.ReplayAll();
presenter.InitialiseView();
mockView.AssertWasCalled(v => v.Property1 = FilterOptions.OptionA);
mockView.AssertWasCalled(v => v.Property2 = Arg<OtherFilterOptions>.Matches(filters =>
(filters.Filter1 == true) && (filters.Filter2 == false)));
}
The answer I was looking for was in the LastCall.Constraints() method. Passing arguments to Constraints allows you to specify property values of an argument:
[Test]
public void TestOne()
{
var mocks = new MockRepository();
var mockView = mocks.CreateMock<IToTestView>();
ToTestPresenter presenter = new ToTestPresenter(mockView);
using (mocks.Ordered())
{
mockView.Property1 = FilterOptions.OptionA;
mockView.Property2 = OtherFilterOptions.DefaultFilterOptions();
LastCall.Constraints(
Property.Value("Filter1", true)
& Property.Value("Filter2", false));
}
mocks.ReplayAll();
presenter.InitialiseView();
mocks.VerifyAll();
}
There are a large number of options that can be passed in to the Constraints() method. Details on some of them on this CodeProject page
Another option is LastCall.IgnoreArguments() if you don't care what the property is actually set to.
I'm having some issues trying to use RhinoMocks to figure out if calling a method in my class under test, a certain number of other methods is being called too.
My class to be tested:
public class OrderMessageHandler : IHandleMessages<UpdateOrder>
{
public virtual IRepository Repository { get; set; }
public void Handle(UpdateOrder message)
{
if (!message.Order.Confirmed) return;
using (var om = new OperationManager())
{
try
{
om.BeginOperation();
LVR.Order.Model.OrderHeader order = ConvertToLocalOrderHeader(message.Order);
Repository.SaveUpdate(order);
om.CommitOperation();
}
catch (Exception ex)
{
om.RollbackOperation();
// other stuff here
}
}
}
internal virtual LVR.Order.Model.OrderHeader ConvertToLocalOrderHeader(Protocol.DTO.OrderHeader order)
{
// do stuff here, and call Repository.GetAll<Country>()
}
}
Here's my test method
[Fact]
public void ConvertToLocalOrderHeader_GivenConfirmedOrderMessage_CallTheConversionMethods()
{
// create a partial mock 'cause I want some of the implementation to be the true sut class
var sut = MockRepository.GeneratePartialMock<OrderMessageHandler>();
// create a stub for the repository, in order to avoid hitting the db
sut.Repository = MockRepository.GenerateStub<IRepository>();
sut.Repository.Stub(r => r.GetAll<Country>())
.Return(
new List<Country>
{
new Country {CountryID = "IT", Description = "Italy"},
new Country {CountryID = "US", Description = "United States"}
}.AsQueryable()
);
sut.Repository.Stub(r => r.SaveUpdate<OrderHeader>(Arg<OrderHeader>.Is.Anything));
// call the method I want to test
sut.Handle(new UpdateOrder
{
Order = order,
EventId = new Guid(),
EventTime = DateTime.Now
});
// verify that the method has been called (this is useless in my real test, I put it here just to understand why it doesn't work)
sut.AssertWasCalled(s => s.Handle(Arg<UpdateOrder>.Is.Anything));
// verify that an inner method (virtual) has been called during the execution of sut.handle()
sut.AssertWasCalled(s => s.ConvertToLocalOrderHeader(order));
}
In the 2 sut.AssertWasCalled calls, I receive an error Object reference not set to an instance of an object. . The reason is that sut.AssertWasCalled make a call to the method i'm verifying... thus
sut.AssertWasCalled(s => s.Handle(Arg<UpdateOrder>.Is.Anything));
calls
sut.Handle(null)
And being null the parameter the method throws an exception.
Too bad the problem is that it shouldn't re-call the method but just tell me if it has never benn called before in the test method.
What's wrong here?
Edit:
as per suggestion received in the comments, I tried a different approach (given that I don't like the expect/verify flavour). Here's the test method:
[Fact]
public void ConvertToLocalOrderHeader_GivenConfirmedOrderMessage_CallTheConversionMethods2()
{
var mocks = new MockRepository();
var sut = mocks.PartialMock<OrderMessageHandler>();
sut.Repository = mocks.Stub<IRepository>();
sut.Repository.Stub(r => r.GetAll<Country>())
.Return(
new List<Country>
{
new Country {CountryID = "IT", Description = "Italy"},
new Country {CountryID = "US", Description = "United States"}
}.AsQueryable()
);
sut.Repository.Stub(r => r.SaveUpdate<OrderHeader>(Arg<OrderHeader>.Is.Anything));
Expect.Call(() => sut.Handle(Arg<UpdateOrder>.Is.Anything));
sut.Replay();
sut.Handle(new UpdateOrder
{
Order = order,
EventId = new Guid(),
EventTime = DateTime.Now
});
mocks.VerifyAll();
}
Adn here's the error I had:
System.InvalidOperationException
Previous method 'OrderMessageHandler.get_Repository();' requires a return value or an exception to throw.
The error is thrown by the line of code
Expect.Call(() => sut.Handle(Arg<UpdateOrder>.Is.Anything));
So, no luck even with this approach...
Any other idea?
I found out what the problem was: the method ConvertToLocalOrderHeader should be declared at least protected internal in order to let Rhinomocks override it. Virtual is not enough, being the testing class a differente class.
Very easy solution for a problem that took me hours to be solved :/
i am new for Nunit.please help for write a test case.
this is my class
public CommandModule(ICommandFetcher fetcher,ICommandBus commandBus)
{
//Get["/"] = p =>
//{z
// return Response.AsText((string)Request.Form.Username);
//};
Post["/"] = parameters =>
{
var commandRequest = this.Bind<MessageEnvelope>();
var command = fetcher.FetchFrom(commandRequest);
commandBus.Send((ICommand)command, commandRequest.MetaData);
return HttpStatusCode.OK;
};
}
}
and i want to test for check this method
commandBus.Send((ICommand)command, commandRequest.MetaData);
thank you!
i try it following way
[Test]
public void whern_reseiving_command_it_sent_to_the_command_bus()
{
var rCommand = new DummyCommand() { SomeProp = 2 };
var serializedCommand = JsonConvert.SerializeObject(rCommand);
var envelope = new MessageEnvelope() { MetaData = new MetaData() { MessageType = "DummyCommand", MessageTypeVersion = 1 }, MessageData = serializedCommand };
var fakeCommand = A.Fake<ICommandBus>();
var browser = new Browser(with =>
{
with.Module<CommandModule>();
with.Dependency<ICommandBus>(fakeCommand);
});
var result = browser.Post("/", with =>
{
with.HttpRequest();
with.JsonBody(envelope);
});
A.CallTo(() => fakeCommand.Send(rCommand,envelope.MetaData)).MustHaveHappened();
but A.CallTo(() => fakeCommand.Send(rCommand,envelope.MetaData)).MustHaveHappened();
it has some kind of error in rcommand value
It sounds like you are looking to explicitly test that ICommandBus.Send is called when your code is executed.
One approach is to mock the ICommandBus dependency. That is, insert a mock object implementing ICommandBus that is able to detect whether that method is called with the right parameter values.
If you take this approach, you would normally do this using a mocking framework (e.g. Moq, or RhinoMocks).
To explain how you would use a mock for this briefly, I will show how you can do this by by explicitly implementing a mock object object yourself that records method calls, and testing them afterwards.
E.g.
MockCommandBus : ICommandBus
{
ICommand PassedCommand { get; set; }
MetaData PassedMetaData { get; set; }
public void Send(ICommand command, MetaData metaData)
{
this.PassedCommand = command;
this.PassedMetaData = metaData;
}
}
Then your test case will look like this:
[TestCase]
public void PostSendsCommandOnBus()
{
// ARRANGE
var mockCommandBus = new MockCommandBus();
ICommand expectedCommand = <whatever you expect>;
MetaData expectedMetaData = <whatever you expect>;
// Code to construct your CommandModule with mockCommandBus.
// ACT
// Code to invoke the method being tested.
// ASSERT
Assert.AreEqual(expectedCommand, mockCommandBus.PassedCommand);
Assert.AreEqual(expectedMetaData , mockCommandBus.PassedMetaData );
}
Caveat:
Note that this is only one approach to unit testing that happens to fit exactly what you are asking for here. Excessive use of mocks to test explicit interaction with dependencies at a low level can lead to the development of very brittle test suites that are testing an underlying implementation rather than behaviour of a system.
I have begun to dabble in unit testing, and have created a couple of tests that test one of my presenter methods. This testing requires a mock of my data access class, and one method in particular. This is the original method from my data access class:
public IEnumerable<IArea> GetAreaList()
{
ConnectToTFSProject();
XmlNode areaNode = GetAreaNode();
List<IArea> areaList = new List<IArea>();
foreach (XmlNode node in areaNode.FirstChild.ChildNodes)
{
IArea area = new Area() { AreaName = node.Attributes["Name"].Value };
areaList.Add(area);
}
areaList.Sort();
return areaList;
}
I would like to test the presenter method with different scenarios, e.g.:
a regular list of areas
an empty list of areas
a list of areas with duplicates
a list or areas containing one empty string area
My first thought was to create a separate mock data access class for each of these scenarios. I thought this to be a little cumbersome, so I adapted the method slightly to allow the reading of different xml files, containing data specific to the current test. Here is how my mock method looks:
public IEnumerable<IArea> GetAreaList(string dataSource)
{
List<IArea> areaList = new List<IArea>();
XmlTextReader areaReader = new XmlTextReader(dataSource);
while (areaReader.Read())
{
if (areaReader.NodeType == XmlNodeType.Text)
areaList.Add(new Area() { AreaName = areaReader.Value });
}
return areaList;
}
This mock method will then be called from the PresenterTest class as follows:
[TestMethod]
public void PopulateAreaComboBox_WithValidAreaList()
{
//Act
_presenter.PopulateAreaComboBox(mockFolderPath + "MockAreaList.xml");
//Assert
Assert.AreEqual(3, _view.AreaListLoaded.Count);
}
[TestMethod]
public void PopulateAreaComboBox_WithEmptyAreaList()
{
//Act
_presenter.PopulateAreaComboBox(mockFolderPath + "MockEmptyAreaList.xml");
//Assert
Assert.AreEqual(0, _view.AreaListLoaded.Count);
}
Now, the problem I have here is that I now need to change the signature of my original method (by adding reference to the dataSource parameter):
public IEnumerable<IArea> GetAreaList(string dataSource)
Because this parameter is required only for the unit tests, the value of null is passed into this method from the real presenter class, and never used.
I know this is wrong but how should this be accomplished? Should I create a separate mock data access class that sets up each test data scenario?
I resolved this by exposing a public string (TestDataXml) in my mock data access class. I then created a new instance of the mock data access class for each test, setting this string to the location of an individual test data xml file each time:
class MockDataRetrieval : IDataRetrieval
{
public string TestDataXml { get; set; }
public IEnumerable<IArea> GetAreaList()
{
List<IArea> areaList = new List<IArea>();
XmlTextReader areaReader = new XmlTextReader(TestDataXml);
while (areaReader.Read())
{
if (areaReader.NodeType == XmlNodeType.Text)
areaList.Add(new Area() { AreaName = areaReader.Value });
}
return areaList;
}
}
To call the mock method from the PresenterTest class:
[TestMethod]
public void PopulateAreaComboBox_WithValidAreaList()
{
//Arrange
_data = new MockDataRetrieval() { TestDataXml = mockFolderPath + "MockAreaList.xml" };
_view = new MockMainForm();
_presenter = new TestCasePresenter(_view, _data);
//Act
_presenter.PopulateAreaComboBox();
//Assert
Assert.AreEqual(3, _view.AreaListLoaded.Count);
}
[TestMethod]
public void PopulateAreaComboBox_WithEmptyAreaList()
{
//Arrange
_data = new MockDataRetrieval() { TestDataXml = mockFolderPath + "MockEmptyAreaList.xml" };
_view = new MockMainForm();
_presenter = new TestCasePresenter(_view, _data);
//Act
_presenter.PopulateAreaComboBox();
//Assert
Assert.AreEqual(0, _view.AreaListLoaded.Count);
}
Ralf: Thanks for your comment. This question relates to testing the Presenter.PopulateAreaComboBox method. The GetAreaList method in this example is from the mock data access class, and simply provides the method under test with test data.