I'm just starting out with BDD/TDD using MSpec (with AutoMocking by James Broome) and RhinoMocks. Here's an excerpt from my practice project:
namespace Tests.VideoStore.Controllers
{
public abstract class context_for_movie_controller :
Specification<MovieController>
{
private static IList<Movie> movies;
protected static IMovieRepository _movieRepository;
protected static ActionResult _result;
protected static string title;
protected static string director;
Establish context = () =>
{
_movieRepository = DependencyOf<IMovieRepository>();
};
}
[Subject(typeof(MovieController))]
public class when_searching_for_movies_with_director :
context_for_movie_controller
{
Establish context = () =>
{
title = null;
director = "James Cameron";
var movie4 = new Movie {
Title = "Terminator", Director = "James Cameron"};
var movie6 = new Movie {
Title = "Avatar", Director = "James Cameron"};
movies = new List<Movie> {movie4, movie6};
// Repository returns all movies.
_movieRepository.Stub(x => x.FindMovies(title, director))
.Return(movies);
};
Because of = () => _result = subject.Find(title, director);
It should_fetch_movies_from_the_repository = () =>
_movieRepository.AssertWasCalled(x =>
x.FindMovies(title, director));
It should_return_a_list_of_movies_matching_the_director = () =>
_result.ShouldBeAView().And()
.ShouldHaveModelOfType<IEnumerable<Movie>>)
.And().ShouldContainOnly(movies);
}
As you can see, I stubbed out the FindMovies() method on the MovieRepository class. Then I'm calling the MoviesController.Find() action. My question is, should there be an assert to check if the stubbed method (FindMovies) was called by the controller? Or perhaps should I only care about the returned result and not where it was taken from? Furthermore, a spec that says "should_fetch_movies_from_the_repository" looks a lot like an engineering task, not something that a client might understand - does it have its place in BDD?
the general rule to follow for assertions is that you assert against output interactions, not input interactions.
the FindMovies stub is returning a "movies" collection to the class that called it and you are then verifying that the class received the correct list via the "it should return a list of movies matching the director" assertion. if the FindMovies method is not called, then this assertion will fail.
therefore, you do not need to assert the calls against the FindMovies method.
to counterpoint this, if you have an mock or stub that is purely output - let's say an IView interface being called by a Presenter class, then you do want to assert against the IView being called. for example, this code:
public class MyPresenter
{
... other code here
public DoSomething()
{
IList data = GetSomeData();
myView.DisplayData(data);
}
}
you would want to assert that the view.DisplayData method is called in this case, because you are not retrieving anything from this call that can be asserted by another test.
as for the "fetch from repository" - of course your customers care about that. they want the system to save movies to the storage and load them from the storage. however ... the FindMovies call is an input into the class being tested, so it's not necessary to have this assetion or test, at all. if the FindMovies method is not called, then the other test will fail and let you know that there is a problem.
Related
I'm having a difficult time trying to understand how to appropriately return mocked data from a simulated database call in a unit test.
Here's an example method I want to unit test (GetBuildings):
public class BuildingService : IBuildingService {
public IQueryable<Building> GetBuildings(int propertyId)
{
IQueryable<Building> buildings;
// Execution path for potential exception thrown
// if (...) throw new SpecialException();
// Another execution path...
// if (...) ...
using (var context = DataContext.Instance())
{
var Params = new List<SqlParameter>
{
new SqlParameter("#PropertyId", propertyId)
};
// I need to return mocked data here...
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
}
return buildings;
}
}
So GetBuildings calls a stored procedure.
So I need to mock the DataContext, that of which I can override and set a testable instance. So what happens here is, in the above example DataContext.Instance() does return the mocked object.
[TestFixture]
public class BuildingServiceTests
{
private Mock<IDataContext> _mockDataContext;
[SetUp]
public void Setup() {
_mockDataContext = new Mock<IDataContext>();
}
[TearDown]
public void TearDown() {
...
}
[Test]
public void SomeTestName() {
_mockDataContext.Setup(r =>
r.ExecuteQuery<Building>(CommandType.StoredProcedure, "someSproc"))
.Returns(new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable());
DataContext.SetTestableInstance(_mockDataContext.Object);
var builings = BuildingService.GetBuildings(1, 1);
// Assert...
}
Please ignore some of the parameters, like propertyId. I've stripped those out and simplified this all. I simply can't get the ExecuteQuery method to return any data.
All other simple peta-poco type methods I can mock without issue (i.e. Get, Insert, Delete).
Update
DataContext.Instance returns the active instance of the DataContext class, if exists, and if not exists, returns a new one. So the method of test under question returns the mocked instance.
Do not mock DataContext. Because mocking DataContext will produce tests tightly coupled to the implementation details of DataContext. And you will be forced to change tests for every change in the code even behavior will remain same.
Instead introduce a "DataService" interface and mock it in the tests for BuildingService.
public interface IDataService
{
IEnumerable<Building> GetBuildings(int propertyId)
}
Then, you can tests implementation of IDataService agains real database as part of integration tests or tests it agains database in memory.
If you can test with "InMemory" database (EF Core or Sqlite) - then even better -> write tests for BuildingService against actual implementation of DataContext.
In tests you should mock only external resources (web service, file system or database) or only resources which makes tests slow.
Not mocking other dependencies will save you time and give freedom while you refactoring your codebase.
After update:
Based on the updated question, where BuildingService have some execution path - you can still testing BuildingService and abstract data related logic to the IDataService.
For example below is BuildingService class
public class BuildingService
{
private readonly IDataService _dataService;
public BuildingService(IDataService dataService)
{
_dataService = dataService;
}
public IEnumerable<Building> GetBuildings(int propertyId)
{
if (propertyId < 0)
{
throw new ArgumentException("Negative id not allowed");
}
if (propertyId == 0)
{
return Enumerable.Empty<Building>();
}
return _myDataService.GetBuildingsOfProperty(int propertyId);
}
}
In tests you will create a mock for IDataService and pass it to the constructor of BuildingService
var fakeDataService = new Mock<IDataContext>();
var serviceUnderTest = new BuildingService(fakeDataService);
Then you will have tests for:
"Should throw exception when property Id is negative"
"Should return empty collection when property Id equals zero"
"Should return collection of expected buildings when valid property Id is given"
For last test case you will mock IDataService to return expected building only when correct propertyId is given to _dataService.GetBuildingsOfProperty method
In order for the mock to return data is needs to be set up to behave as expected given a provided input.
currently in the method under test it is being called like this
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
Yet in the test the mock context is being setup like
_mockDataContext.Setup(r =>
r.ExecuteQuery<Building>(CommandType.StoredProcedure, "someSproc"))
.Returns(new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable());
Note what the mock is told to expect as parameters.
The mock will only behave as expected when provided with those parameters. Otherwise it will return null.
Consider the following example of how the test can be exercised based on the code provided in the original question.
[Test]
public void SomeTestName() {
//Arrange
var expected = new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable();
_mockDataContext
.Setup(_ => _.ExecuteQuery<Building>(CommandType.StoredProcedure, It.IsAny<string>(), It.IsAny<object[]>()))
.Returns(expected);
DataContext.SetTestableInstance(_mockDataContext.Object);
var subject = new BuildingService();
//Act
var actual = subject.GetBuildings(1);
// Assert...
CollectionAssert.AreEquivalent(expected, actual);
}
That said, the current design of the system under test is tightly coupled to a static dependency which is a code smell and makes the current design follow some bad practices.
The static DataContext which is currently being used as a factory should be refactored as such,
public interface IDataContextFactory {
IDataContext CreateInstance();
}
and explicitly injected into dependent classes instead of calling the static factory method
public class BuildingService : IBuildingService {
private readonly IDataContextFactory factory;
public BuildingService(IDataContextFactory factory) {
this.factory = factory
}
public IQueryable<Building> GetBuildings(int propertyId) {
IQueryable<Building> buildings;
using (var context = factory.CreateInstance()) {
var Params = new List<SqlParameter> {
new SqlParameter("#PropertyId", propertyId)
};
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
}
return buildings;
}
}
This will allow for a proper mock to be created in injected into the subject under test without using a static workaround hack.
[Test]
public void SomeTestName() {
//Arrange
var expected = new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable();
_mockDataContext
.Setup(_ => _.ExecuteQuery<Building>(CommandType.StoredProcedure, It.IsAny<string>(), It.IsAny<object[]>()))
.Returns(expected);
var factoryMock = new Mock<IDataContextFactory>();
factoryMock
.Setup(_ => _.CreateInstance())
.Returns(_mockDataContext.Object);
var subject = new BuildingService(factoryMock.Object);
//Act
var actual = subject.GetBuildings(1);
// Assert...
CollectionAssert.AreEquivalent(expected, actual);
}
This question is posted as a follow up to How do you Mock an class for a unit test that has a return type but no input parameters
Since asking the original question I have now created a Minimal, Complete and Verifiable Example which is used as the basis for this question.
I have a controller (shown below)
public class HomeController : Controller
{
private OrganisationLogic _organisationLogic;
public HomeController(OrganisationLogic logic)
{
_organisationLogic = new OrganisationLogic();
}
public ActionResult Index()
{
var model = _organisationLogic.GetOrganisation().ToViewModel();
return View(model);
}
}
The controller retrieves data from a method in a Business Logic Layer called OrganisationLogic (shown below)
public class OrganisationLogic : LogicRepository<OrganisationModel>
{
public OrganisationLogic()
{
}
public override OrganisationModel GetOrganisation()
{
return new OrganisationModel { Id = 1, OrganisationName = "My Orgaisation", Address = "Logic" };
}
}
The business logic later inherits Logic repository (shown below)
public abstract class LogicRepository<T> : ILogicRepository<T>
{
protected LogicRepository()
{
}
public abstract T GetOrganisation();
}
The Logic Repository implements the ILogicRepository Interface (shown below)
public interface ILogicRepository<TModel>
{
TModel GetOrganisation();
}
I want to Unit Test the HomeController to verify that the data displayed in the ViewModel is returned correctly from OrganisationLogic and Transformed from OrganisationModel into an OrganisationViewModel.
I have written the following UnitTest which uses Moq to mock the method _OrganisationLogic.GetOrganisation().
[TestMethod]
public void Index()
{
var _OrganisationLogic = new Mock<OrganisationLogic>();
var testdata = new OrganisationModel { Id = 3, OrganisationName = "My Test Class Organisation" };
_OrganisationLogic.Setup(p => p.GetOrganisation()).Returns(testdata).Callback<OrganisationModel>(p=>p = testdata);
HomeController controller = new HomeController(_OrganisationLogic.Object);
ViewResult result = controller.Index() as ViewResult;
OrganisationViewModel model = (OrganisationViewModel)result.Model;
Assert.AreEqual(testdata.OrganisationName,model.OrganisationName);
}
When I run the test the test fails. The reason for this is that the Mock has not overridden the class and has instead returned the result from the actual method in the BusinessLogic Layer.
In my original question I posted that the error message being generated was:
System.ArgumentException Invalid callback. Setup on method with 0 parameter(s) cannot invoke callback with different number of parameters (1). Source=Moq StackTrace: at Moq.MethodCallReturn2.ValidateNumberOfCallbackParameters(MethodInfo
callbackMethod) at Moq.MethodCallReturn2.ValidateReturnDelegate(Delegate callback) at Moq.MethodCallReturn2.Returns[T](Func2 valueExpression)
I have now been able to replicate this error message exactly by running the following Unit Test. I suspect the unit test above is closer to what I need and that in the instance below I am setting up the Return() incorrectly. Thoughts on this are welcome?
[TestMethod]
public void Index()
{
var _OrganisationLogic = new Mock<OrganisationLogic>();
var testdata = new OrganisationModel { Id = 3, OrganisationName = "My Test Class Organisation" };
_OrganisationLogic.Setup(p => p.GetOrganisation()).Returns<OrganisationModel>(p=>p = testdata).Callback<OrganisationModel>(p=>p = testdata);
HomeController controller = new HomeController(_OrganisationLogic.Object);
ViewResult result = controller.Index() as ViewResult;
OrganisationViewModel model = (OrganisationViewModel)result.Model;
Assert.AreEqual(testdata.OrganisationName,model.OrganisationName);
}
My question is how to I setup the Mock so that it uses my test data.
In order to assist with answering the the above I have placed a version of the Code on GitHub that demonstrates this issue and shows the test failing. This can be accessed at https://github.com/stephenwestgarth/UnitTestExample
Any help would be very much appreciated.
Change the constructor of HomeController so that the parameter type is ILogicRepository<OrganisationModel>, and the field will need that type as well, and use the instance that was injected into the constructor. _organisationLogic = logic; (your code above ignores the parameter and creates its own concrete instance of the OrganisationLogic, which means it is not using your mock object.
In the test change the declaration of _OrganisationLogic to be...
var _OrganisationLogic = new Mock<ILogicRepository<OrganisationModel>>();
As I said when you previously asked, I don't think you need that Callback in there.
Edited constructor would look like this...
private ILogicRepository<OrganisationModel> _organisationLogic;
public HomeController(ILogicRepository<OrganisationModel> logic)
{
_organisationLogic = logic;
}
I'm new in tests' scope and i want to write test to this business logic's function using FakeItEasy.
In My StudentsBusinessLogic code i want to test the function GetOldestStudent.
List item
Code:
public class StudentsBusinessLogic:IStudentsBusinessLogic
{
private IStudentRepository _studentRepository;
public StudentsBusinessLogic()
{
this._studentRepository = new DalConcreteFactory().GetStudentsRepository();
}
//I want to test this function
public Student GetOldestStudent()
{
var q1 = from students in this._studentRepository.AllStudents()
where students.Age >= ((from oldest in this._studentRepository.AllStudents()
select oldest.Age).Max())
select students;
Student result = q1.First();
Console.WriteLine(result.Age);
return result;
}
}
Now, i have to mock that code snippet: this._studentRepository.AllStudents(),
because i don't like to use the this._studentRepository.AllStudents() (that uses the original db).
My question is: How to test GetOldestStudent with mocking studentRepository.AllStudents() call.
The test i have tried to write is:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
// Arrange
var fakeStuRep = A.Fake<IStudentRepository>();
var fakeFactory = A.Fake<DalAbstractFactory>();
A.CallTo(() => fakeStuRep.AllStudents()).Returns(new System.Collections.Generic.List<BE.Student> { new BE.Student { ID = 1, Age = 7 }, new BE.Student {ID = 2, Age = 55}});
A.CallTo(() => fakeFactory.GetStudentsRepository()).Returns(null);
// Act
IStudentsBusinessLogic bl = new StudentsBusinessLogic(true);
var res = bl.GetOldestStudent();
// Assert
Assert.AreEqual(55, res.Age);
}
}
Unfortunately, that test leads to run-time exception due to problem in IStudentRepository ctor (specific issue that not related to this scope). But what i've tried to do is to skip on IStudentRepository initializing stage and Instead - to mocks it.
Can some one help me how to do it correct?
You need to break the concrete dependency between your business logic class and the repository, example:
public class StudentsBusinessLogic:IStudentsBusinessLogic
{
private IStudentRepository _studentRepository;
public StudentsBusinessLogic(IStudentRepository studentRepository)
{
this._studentRepository = studentRepository;
}
...
Now you can pass the mocked instance of the repository to your class:
var fakeStuRep = A.Fake<IStudentRepository>();
A.CallTo(() => fakeStuRep.AllStudents()).Returns(new System.Collections.Generic.List<BE.Student> { new BE.Student { ID = 1, Age = 7 }, new BE.Student {ID = 2, Age = 55}});
IStudentsBusinessLogic bl = new StudentsBusinessLogic(fakeStuRep);
var res = bl.GetOldestStudent();
Finally, you have your mock well defined, initialised and passed to the concrete class with the business logic.
This is one way to unit test your business logic class. You don't want (at least not now) to call the real repository or any concrete DAL implementation.
Note: your test should assert that the method from the mocked repository was called. FakeItEasy provide several ways to check that.
My apologies in advanced for not knowing the technical name of this scenario. I am mocking for unit test and that is all fine. However on this section of code I have run into a scenario that exceeds my mocking knowledge. Basically I have MethodA that takes 3 parameters. One of the parameters is passed as another method's output.
When I step through the method passed as a parameter is executed
My difficulty is that the passed method is being executed BEFORE my mocked object. Now it seems like a simple solution...mock the second method as well...that is where my knowledge falls down. I don't know how to get the "second" method mock into the testing context.
My controller being tested (simplified of course):
public class OrderController : ApiController
{
public OrderController(IRepositoryK repositoryk)
{}
public HttpResponseMessage NewOrder()
{
...snip....
string x = repositoryk.MethodA("stuff", "moreStuff", MethodB("junk"));
}
public string MethodB(string data)
{
using (var client = new HttpClient())
{...make call to Google API...}
}
}
My test:
[TestMethod]
public void AddOrder_CorrectResponse()
{
private Mock<IRepositoryK> _repK = new Mock<IRepositoryK>();
_repK.Setup(x => x.MethodA(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns("Yippe");
//of course I've left out all the controller buildup and execution stuff.
}
So I really have no desire to dive into MethodB but it seems to be doing it anyway. What am I doing wrong?
TIA
Thank you for your responses. I understand completely what you are saying. I'm trying to get some testing coverage in place before refactoring. So is there no way of keeping methodB from executing and just let my repositoryK mock just return what I've specified in the setup.
Your code is not easy to test, because it has hard dependency on HttpClient. You have nicely separated repository implementation, but if you want to easily test the code you should also separate code which calls Google API. The idea is to have something like this:
// Add interfece for accessing Google API
public interface IGoogleClient
{
string GetData(string data);
}
// Then implementation is identical to MethodB implementation:
public class GoogleClient : IGoogleClient
{
public string GetData(string data)
{
using (var client = new HttpClient())
{
//...make call to Google API...
}
}
}
// Your controller should look like this:
public class OrderController : ApiController
{
private readonly IRepositoryK repositoryk;
private readonly IGoogleClient googleClient;
public OrderController(IRepositoryK repositoryk, IGoogleClient googleClient)
{
this.googleClient = googleClient;
this.repositoryk = repositoryk;
}
public HttpResponseMessage NewOrder()
{
//...snip....
string x = repositoryk.MethodA("stuff", "moreStuff", MethodB("junk"));
}
public string MethodB(string data)
{
return googleClient.GetData(data);
}
}
If you have such setup you can easily mock both IRepositoryK and IGoogleClient:
Mock<IRepositoryK> repK = new Mock<IRepositoryK>();
Mock<IGoogleClient> googleClient = new Mock<IGoogleClient>();
repK.Setup(x => x.MethodA(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns("Yippe");
googleClient.Setup(It.IsAny<string>()).Returns("something");
var controller = new OrderController(repK.Object, googleClient.Object);
// Test what you want on controller object
However, if you want to keep your code tightly coupled you can mock the call to MethodB with small changes.
First, you need to make method MethodB virtual, so it could be overridden in mock:
public virtual string MethodB(string data)
{
// your code
}
Then in your test instead of instantiating controller, instantiate and use mock of your controller:
var repK = new Mock<IRepositoryK>();
// create mock and pass the same constructor parameters as actual object
var controllerMock = new Mock<OrderController>(repK.Object);
controllerMock.CallBase = true;
// mock MethodB method:
controllerMock.Setup(x => x.MethodB(It.IsAny<string>())).Returns("data");
// call the method on mock object
// instead of calling MethodB you will get a mocked result
var result = controllerMock.Object.NewOrder();
[unit testing newbie] [c#]
Consider the following scenario:
I'm using Silverlight and calling a WCF service. Silverlight can only call WCF services asynchronously. I build a wrapper around the WCF service so that I can work with Action parameters. (makes the client code a lot cleaner).
So I have an async service that retrieves meeting rooms.
public interface IMeetingRoomService
{
void GetRooms(Action<List<MeetingRoom>> result);
}
Turning GetRooms into List<MeetingRoom> GetRooms() is not an option.
I want to use this service in a ViewModel to set a public property called Rooms.
public class SomeViewModel
{
private readonly IMeetingRoomService _meetingRoomService;
public List<MeetingRoom> Rooms { get; set; }
public SomeViewModel(IMeetingRoomService meetingRoomService)
{
this._meetingRoomService = meetingRoomService;
}
public void GetRooms()
{
// Code that calls the service and sets this.Rooms
_meetingRoomService.GetRooms(result => Rooms = result);
}
}
I want to unit test the implementation of SomeViewModel.GetRooms().
(For this question I quickly wrote the implementation but I'm actually trying to use TDD.)
How do I finish this test?
I'm using NUnit and Moq.
[Test]
public void GetRooms_ShouldSetRooms()
{
var theRooms = new List<MeetingRoom>
{
new MeetingRoom(1, "some room"),
new MeetingRoom(2, "some other room"),
};
var meetingRoomService = new Mock<IMeetingRoomService>();
//How do I setup meetingRoomService so that it gives theRooms in the Action??
var viewModel = new SomeViewModel(meetingRoomService.Object);
viewModel.GetRooms();
Assert.AreEqual(theRooms, viewModel .Rooms);
}
EDIT:
Solution
Read Stephane's answer first.
This is the Test code I ended up writing thanks to stephane's answer:
[Test]
public void GetRooms_ShouldSetRooms()
{
var meetingRoomService = new Mock<IMeetingRoomService>();
var shell = new ShellViewModel(meetingRoomService.Object);
var theRooms = new List<MeetingRoom>
{
new MeetingRoom(1, "some room"),
new MeetingRoom(2, "some other room"),
};
meetingRoomService
.Setup(service => service.GetRooms(It.IsAny<Action<List<MeetingRoom>>>()))
.Callback((Action<List<MeetingRoom>> action) => action(theRooms));
shell.GetRooms();
Assert.AreEqual(theRooms, shell.Rooms);
}
Here is some pseudo code, I haven't run it. But I think that's what you want.
SetupCallback is what you are interested in.
For all the calls to _meetingRoomServiceFake.GetRooms, simply set the _getRoomsCallback to the parameter passed in.
You now have a reference to the callback that you are passing in your viewmodel, and you can call it with whatever list of MeetingRooms you want to test it.
So you can test your asynchronous code almost the same way as synchronous code. it's just a bit more ceremony to setup the fake.
Action<List<MeetingRoom>> _getRoomsCallback = null;
IMeetingRoomService _meetingRoomServiceFake;
private void SetupCallback()
{
Mock.Get(_meetingRoomServiceFake)
.Setup(f => f.GetRooms(It.IsAny<Action<List<MeetingRoom>>>()))
.Callback((Action<List<MeetingRoom>> cb) => _getRoomsCallback= cb);
}
[Setup]
public void Setup()
{
_meetingRoomServiceFake = Mock.Of<IMeetingRoomService>();
SetupCallback();
}
[Test]
public void Test()
{
var viewModel = new SomeViewModel(_meetingRoomServiceFake)
//in there the mock gets called and sets the _getRoomsCallback field.
viewModel.GetRooms();
var theRooms = new List<MeetingRoom>
{
new MeetingRoom(1, "some room"),
new MeetingRoom(2, "some other room"),
};
//this will call whatever was passed as callback in your viewModel.
_getRoomsCallback(theRooms);
}
You could use an AutoResetEvent to handle async calls.
Just initialize it as unset and configure your mock service to set it in the callback.
(IE:
var mockService = new Mock();
mockService.SetUp(x => x.MyMethod()).Returns(someStuff).Callback(() => handle.Set());
)
After that I use hadle.WaitOne(1000) to check if it was called. (IMHO 1000 miliseconds are more than enough to run the async code).
Sorry: This was supposed to go as a reply to the post above... I can't for the life of me figure out how to reply :)