FakeItEasy says MustHaveHappened didn't happen ... but it did - c#

I'm trying to unit test a "service layer" / "application facade layer" method. This is the method I'm trying to unit test:
// Create a new order in the database for a customer. Given a customer id,
// will create a new order and return an OrderDto for use in the presentation
// layer.
public OrderDto CreateOrderForCustomer(int customerId)
{
// Find the customer
var customer = _customerRepository.GetCustomerById(customerId);
// Create an order and apply special logic to get it ready for use.
var orderFactory = new OrderFactory();
var order = orderFactory.CreateOrder(customer);
// IMPORTANT: This is what I'm trying to unit test ...
_orderRepository.Save(order);
order.Status = "Editing";
// Using AutoMapper to turn this into a DTO that will be returned
// to the Presentation layer. The Mappings are created in the
// constructor and not depicted in this code snippet.
var orderDto = Mapper.Map<Order, OrderDto>(order);
return orderDto;
}
(Note ... I've added copious notes here for clarity. I'm not usually this chatty.)
Since this method's job is to orchestrate Domain Layer methods and Persistence Layer methods into creating an empty order, persist it, and return it as a simple DTO, I figured this was a great job for FakeItEasy ... I'll just make sure those critical methods are being orchestrated properly making sure they get called using FakeItEasy's MustHaveHappened().
So, with that in mind, here's the unit test I created:
[TestMethod]
public void CreateOrderForCustomer_ValidCustomer_CreatesNewOrder()
{
// Arrange
var customer = _customerRepository.GetCustomerById(1);
Assert.AreEqual(0, customer.Orders.Count);
// Act
var orderDto = _orderEntryService.CreateOrderForCustomer(1);
// Assert
// Here I'm trying to make sure to re-create the order that was actually
// sent into the _customerRepository.Save() ... I should be able to
// simple un-map the OrderDto back to an Order, and undo the property
// change.
var order = Mapper.Map<OrderDto, Order>(orderDto);
order.Status = "New";
A.CallTo(() => _customerRepository.GetCustomerById(1)).MustHaveHappened();
// **THIS CAUSES AN EXCEPTION**
A.CallTo(() => _orderRepository.Save(order)).MustHaveHappened();
Assert.AreEqual(1, customer.Orders.Count);
}
In the unit test, I can't access the ACTUAL Order that was created in the method under test, I try to do the next best thing ... take the DTO version of the Order that is RETURNED by the method under test, map the DTO version of the Order back out to a new instance of the domain model Order and make sure the properties are the same before sending it to FakeItEasy's MustHaveHappened().
I've debugged the unit test and eyed up the ACTUAL Order's properties versus the FAKED Order's properties ... I assure you, they are identical. Also, I can confirm, through debugging, that the _customerRepository.Save(order) is indeed being called.
QUESTION
Is .MustHaveHappened() failing because I'm essentially sending in two different instances of the Order object -- even though their properties are identical? Even though the properties are the same, does FakeItEasy need the same instance of the input parameter to ensure that the method call has happened?
Furthermore, any suggestions for how I should be testing this sort of thing (i.e., an orchestration / service / "application facade" / what-ever-you-want-to-call-it layer method)?

Is .MustHaveHappened() failing because I'm essentially sending in two different instances of the Order object -- even though their properties are identical?
Yes. FakeItEasy will use .Equals, which (unless your class overrides it) for reference types defaults to reference equality.
(...) does FakeItEasy need the same instance of the input parameter to ensure that the method call has happened?
No. You can do custom argument matching like this:
A.CallTo(() => _orderRepository.Save(A<Order>.That.Matches(o =>
o.Status == "New" &&
o.Id == 10
))).MustHaveHappened();
But, this problem revealed an issue with your code. From your sample it's clear you're injecting _customerRepository as a dependency. That's great. Why don't you do the same with OrderFactory? If it were injected via interface/base class dependency, you could then easily mock (fake) it and your current problem wouldn't exist.
If you can alter your code I'd suggest injecting the factory (following simple guideline - "no news is good news!"). If not, use custom matchers to verify order properties like I did in the sample above.

Related

Proper way to avoid duplicating test data for unit tests

Lets say that I have the following Unit Test for Entity Framework 6 using Moq:
public void Save_Employee_via_context()
{
var MockContext = new Mock<DcmDataContext>();
var MockSet = new Mock<DbSet<Employee>>();
MockContext.Setup(m => m.Employees).Returns(MockSet.Object);
var service = new GeneralService(MockContext.Object);
//Test valid inputs
for (int i = 0; i < TestData.ValidEmployees.Count; i++)
{
service.AddEmployee(TestData.ValidEmployees[i]);
//veryfy that it was properly inserted
Assert.AreEqual(TestData.ValidEmployees[i],MockSet.Object.Find(TestData.ValidEmployees[i].EmployeeID));
}
//Ensure that the proper methods were called each time. It is implied that this happened if the above
//Assert methods passed, but double checking never killed anybody
MockSet.Verify(m => m.Add(It.IsAny<Employee>()), Times.Exactly(TestData.ValidEmployees.Count));
MockContext.Verify(m => m.SaveChanges(), Times.Exactly(TestData.ValidEmployees.Count));
//Test invalid Inputs
MockSet = new Mock<DbSet<Employee>>();
//InvalidEmployees is a Dictionary<Employee,Type>, where Type is the type of exeption that should eb thrown if
//You attempt to add that Employee
foreach (var pair in TestData.InvalidEmployees)
{
try
{
service.AddEmployee(pair.Key);
//AddEmployee *SHOULD* throw an exception here here.. if not...
Assert.Fail();
}
catch (Exception ex)
{
//Was it the exception that I was expecting to catch?
Assert.Equals(ex.GetType(), pair.Value);
}
}
//ensure that nothing new has been added (redundant, I know, but it doesn't hurt)
MockSet.Verify(m => m.Add(It.IsAny<Employee>()), Times.Never);
MockContext.Verify(m => m.SaveChanges(), Times.Exactly(TestData.ValidEmployees.Count));
}
TestData is a Static Class that I have that holds lists for each model type that I want to test, along with several test cases for each, with both valid and invalid inputs.
I created my test like this because my objects can be fairly large (Employee, for example, has around 15 properties), and as such there is a wide array of test cases that I want to run in order for each test to be thorough. I didn't want to copy/paste, each array of test sample data every single method that needs it, so I wanted to store it in a static container.
I feel like this poses a problem, however. For instance, one of the properties of Employee is Position. You know, what job they have. it is a required property, and an exception should be thrown if that position is either null, or doesn't already exist in the database. That means that in order for the above test to be valid, I'm going to need some mock positions to be in there as well. Oh, but each Position has a Department Attribute... so that needs to be set up too...
Do you see where I am going with this? How can I properly test my code without a full suite of test data to test it against? Well then, I suppose I'll have to write a full suite of test data. Which I did.
The problem is, where do I put it? I decided to put in all in the TestData Class.
This, however, presents a set of problems. Initialization is the biggest one, because I feel like I have to neuter my test data in order to make initialization even remotely feasible. For instance, all of my navigation properties are probably going to have to be null. How could I have my ValidEmployees each have a List<Clients>, and each Client have an assigned Employee without, once again, hard duplicating each Employee as a property of Client, and in the List<Employee> that each Position is going to have. wouldn't it be nice to have Clients = {ValidClients[0],ValidClients[1] within ValidEmployees and SalesRepresentative = ValidEmployees[0] within ValidClients?
I also feel like I need that Navigation data. will
Assert.AreEqual
(
TestData.ValidEmployees[i],
MockSet.Object.Find(TestData.ValidEmployees[i].EmployeeID
)
still return true if ValidEmployees doesn't have navigationData in it? Does this mean I should find another way of ensuring state?
Anyway, these are the problems that I am running into. Am I just setting up my Unit tests completely wrong? How else am I supposed to get robust, independent, DRY and accurate unit tests? What am I missing here?
Any help is appreciated, even if it means starting from scratch with a different mindset. This is my first project where I am taking testing very seriously, but I feel like it isn't going so well. As such, sorry for the wall of text. Sometimes I feel like i'm not asking the right question to get where I want to go.
To answer your second part of the question (about test data). I would not use a test data class for my tests, this will make the tests fragile and may introduce subtle bugs as a change to the test data class may affect numerous unrelated tests in different test classes, google Object Mother Anti-Pattern.
I went down the Object Mother route before and ended up with a whole project containing all my test data. When I needed a new variation of the test data, I kept altering/adding to the object mother. Needless to say, the project became bloated and unmaintainable very quickly. Also, the fact that some unit tests started failing because of these changes (as there is a dependency on this shared data), and the extra time spent to fix them, made it a real annoyance. Hence, I decided that a better approach is to let unit test classes own the data they use (in other words, the test data is contained within the Test Fixture). To achieve this, I introduced a test data builder project but this meant that I have another project to maintain and change (when needed) whenever I write a unit test. Honestly, I'd rather concentrate on the unit tests themselves and not worry about the plumbing and that's why I started using a test data generator. I'm using NBuilder but I've heard very good things about AutoFixture as well. These will allow you to build your test data, concentrate on the parts that are relevant to the behaviour you're testing and let the builder generate random data for the ones that are not (you can control/override the random generators). I'm of the opinion that in order to improve the unit test readability, you should only show (or emphasise) the data that affects the behaviour that you're testing rather than bloat your unit test with irrelevant information.
Example:
var validEmployees = Builder<Employees>.CreateListOfSize(10)
.All()
.With(x => x.IsActive = true)
.And(x => x.LeaveDate = null)
.Build();
Looking at your tests, it appears that your service.AddEmployee does too much :). You can split the validation logic, etc.
Consider test like this (to lead your design). This uses a FakeDbSet:
[Test]
public void Should_add_any_valid_employees_and_save_them()
{
//arrange
var validator = new Mock<IEmployeeValidator>();
validator.Setup(v => v.Validate(It.IsAny<Employee>())).Returns(true);
// ... setup the context and the dbset
var service = new MyService(validator.Object, mockContext.Object)
var newData = new List<EmployeeDto>
{
new EmployeeDto{Id = 1},
new EmployeeDto{Id = 2}
}
// act
service.AddEmployees(newData);
// assert
mockContext.Verify(c => c.SaveChanges(), Times.Once());
Assert.True(fakeDbSet.Count == newData.Count);
CollectionAssert.AreEquivalent( newData.Select(e=>e.Id), mockData.Select(e=>e.Id));
}
[Test]
public void Should_not_add_any_invalid_employees()
{
//arrange
var validator = new Mock<IEmployeeValidator>();
validator.Setup(v => v.Validate(It.IsAny<Employee>())).Returns(false);
// ... setup the context and the dbset
var service = new MyService(validator.Object, mockContext.Object)
var newData = new List<EmployeeDto>
{
new EmployeeDto{Id = 1},
new EmployeeDto{Id = 2}
}
// act
service.AddEmployees(newData);
// assert
mockContext.Verify(c => c.SaveChanges(), Times.Never());
Assert.True(fakeDbSet.Count == 0);
CollectionAssert.IsEmpty( mockData );
}
You can throw in the mix IEmployeeDtoToEmployee mapper as well, so you abstract this part of the functionality as well.

How to handle setting up complex unit test and have them only test the unit

I have a method that takes 5 parameters. This method is used to take a bunch of gathered information and send it to my server.
I am writing a unit test for this method, but I am hitting a bit of a snag. Several of the parameters are Lists<> of classes that take some doing to setup correctly. I have methods that set them up correctly in other units (production code units). But if I call those then I am kind of breaking the whole idea of a unit test (to only hit one "unit").
So.... what do I do? Do I duplicate the code that sets up these objects in my Test Project (in a helper method) or do I start calling production code to setup these objects?
Here is hypothetical example to try and make this clearer:
File: UserDemographics.cs
class UserDemographics
{
// A bunch of user demographic here
// and values that get set as a user gets added to a group.
}
File: UserGroups.cs
class UserGroups
{
// A bunch of variables that change based on
// the demographics of the users put into them.
public AddUserDemographicsToGroup(UserDemographcis userDemographics)
{}
}
File: UserSetupEvent.cs
class UserSetupEvent
{
// An event to record the registering of a user
// Is highly dependant on UserDemographics and semi dependant on UserGroups
public SetupUserEvent(List<UserDemographics> userDemographics,
List<UserGroup> userGroups)
{}
}
file: Communications.cs
class Communications
{
public SendUserInfoToServer(SendingEvent sendingEvent,
List<UserDemographics> userDemographics,
List<UserGroup> userGroups,
List<UserSetupEvent> userSetupEvents)
{}
}
So the question is: To unit test SendUserInfoToServer should I duplicate SetupUserEvent and AddUserDemographicsToGroup in my test project, or should I just call them to help me setup some "real" parameters?
You need test duplicates.
You're correct that unit tests should not call out to other methods, so you need to "fake" the dependencies. This can be done one of two ways:
Manually written test duplicates
Mocking
Test duplicates allow you to isolate your method under test from its dependencies.
I use Moq for mocking. Your unit test should send in "dummy" parameter values, or statically defined values you can use to test control flow:
public class MyTestObject
{
public List<Thingie> GetTestThingies()
{
yield return new Thingie() {id = 1};
yield return new Thingie() {id = 2};
yield return new Thingie() {id = 3};
}
}
If the method calls out to any other classes/methods, use mocks (aka "fakes"). Mocks are dynamically-generated objects based on virtual methods or interfaces:
Mock<IRepository> repMock = new Mock<IRepository>();
MyPage obj = new MyPage() //let's pretend this is ASP.NET
obj.IRepository = repMock.Object;
repMock.Setup(r => r.FindById(1)).Returns(MyTestObject.GetThingies().First());
var thingie = MyPage.GetThingie(1);
The Mock object above uses the Setup method to return the same result for the call defined in the r => r.FindById(1) lambda. This is called an expecation. This allows you to test only the code in your method, without actually calling out to any dependent classes.
Once you've set up your test this way, you can use Moq's features to confirm that everything happened the way it was supposed to:
//did we get the instance we expected?
Assert.AreEqual(thingie.Id, MyTestObject.GetThingies().First().Id);
//was a method called?
repMock.Verify(r => r.FindById(1));
The Verify method allows you to test whether a method was called. Together, these facilities allow you focus your unit tests on a single method at a time.
Sounds like your units are too tightly coupled (at least from a quick view at your problem). What makes me curious is for instance the fact that your UserGroups takes a UserDemographics and your UserSetupEvent takes a list of UserGroup including a list of UserDemographics (again). Shouldn't the List<UserGroup> already include the ÙserDemographics passed in it's constructor or am I misunderstanding it?
Somehow it seems like a design problem of your class model which in turn makes it difficult to unit test. Difficult setup procedures are a code smell indicating high coupling :)
Bringing in interfaces is what I would prefer. Then you can mock the used classes and you don't have to duplicate code (which violates the Don't Repeat Yourself principle) and you don't have to use the original implementations in the unit tests for the Communications class.
You should use mock objects, basically your unit test should probably just generate some fake data that looks like real data instead of calling into the real code, this way you can isolate the test and have predictable test results.
You can make use of a tool called NBuilder to generate test data. It has a very good fluent interface and is very easy to use. If your tests need to build lists this works even better. You can read more about it here.

Unit testing, mocking - simple case: Service - Repository

Consider a following chunk of service:
public class ProductService : IProductService {
private IProductRepository _productRepository;
// Some initlization stuff
public Product GetProduct(int id) {
try {
return _productRepository.GetProduct(id);
} catch (Exception e) {
// log, wrap then throw
}
}
}
Let's consider a simple unit test:
[Test]
public void GetProduct_return_the_same_product_as_getProduct_on_productRepository() {
var product = EntityGenerator.Product();
_productRepositoryMock.Setup(pr => pr.GetProduct(product.Id)).Returns(product);
Product returnedProduct = _productService.GetProduct(product.Id);
Assert.AreEqual(product, returnedProduct);
_productRepositoryMock.VerifyAll();
}
At first it seems that this test is ok. But let's change our service method a little bit:
public Product GetProduct(int id) {
try {
var product = _productRepository.GetProduct(id);
product.Owner = "totallyDifferentOwner";
return product;
} catch (Exception e) {
// log, wrap then throw
}
}
How to rewrite a given test that it'd pass with the first service method and fail with a second one?
How do you handle this kind of simple scenarios?
HINT 1: A given test is bad coz product and returnedProduct is actually the same reference.
HINT 2: Implementing equality members (object.equals) is not the solution.
HINT 3: As for now, I create a clone of the Product instance (expectedProduct) with AutoMapper - but I don't like this solution.
HINT 4: I'm not testing that the SUT does NOT do sth. I'm trying to test that SUT DOES return the same object as it is returned from repository.
Personally, I wouldn't care about this. The test should make sure that the code is doing what you intend. It's very hard to test what code is not doing, I wouldn't bother in this case.
The test actually should just look like this:
[Test]
public void GetProduct_GetsProductFromRepository()
{
var product = EntityGenerator.Product();
_productRepositoryMock
.Setup(pr => pr.GetProduct(product.Id))
.Returns(product);
Product returnedProduct = _productService.GetProduct(product.Id);
Assert.AreSame(product, returnedProduct);
}
I mean, it's one line of code you are testing.
Why don't you mock the product as well as the productRepository?
If you mock the product using a strict mock, you will get a failure when the repository touches your product.
If this is a completely ridiculous idea, can you please explain why? Honestly, I'd like to learn.
One way of thinking of unit tests is as coded specifications. When you use the EntityGenerator to produce instances both for the Test and for the actual service, your test can be seen to express the requirement
The Service uses the EntityGenerator to produce Product instances.
This is what your test verifies. It's underspecified because it doesn't mention if modifications are allowed or not. If we say
The Service uses the EntityGenerator to produce Product instances, which cannot be modified.
Then we get a hint as to the test changes needed to capture the error:
var product = EntityGenerator.Product();
// [ Change ]
var originalOwner = product.Owner;
// assuming owner is an immutable value object, like String
// [...] - record other properties as well.
Product returnedProduct = _productService.GetProduct(product.Id);
Assert.AreEqual(product, returnedProduct);
// [ Change ] verify the product is equivalent to the original spec
Assert.AreEqual(originalOwner, returnedProduct.Owner);
// [...] - test other properties as well
(The change is that we retrieve the owner from the freshly created Product and check the owner from the Product returned from the service.)
This embodies the fact that the Owner and other product properties must equal the the original value from the generator. This may seem like I'm stating the obvious, since the code is pretty trivial, but it runs quite deep if you think in terms of requirement specifications.
I often "test my tests" by stipulating "if I change this line of code, tweak a critical constant or two, or inject a few code burps (e.g. changing != to ==), which test will capture the error?" Doing it for real finds if there is a test that captures the problem. Sometimes not, in which case it's time to look at the requirements implicit in the tests, and see how we can tighten them up. In projects with no real requirements capture/analysis this can be a useful tool to toughen up tests so they fail when unexpected changes occur.
Of course, you have to be pragmatic. You can't reasonably expect to handle all changes - some will simply be absurd and the program will crash. But logical changes like the Owner change are good candidates for test strengthening.
By dragging talk of requirements into a simple coding fix, some may think I've gone off the deep end, but thorough requirements help produce thorough tests, and if you have no requirements, then you need to work doubly hard to make sure your tests are thorough, since you're implicitly doing requirements capture as you write the tests.
EDIT: I'm answering this from within the contraints set in the question. Given a free choice, I would suggest not using the EntityGenerator to create Product test instances, and instead create them "by hand" and use an equality comparison. Or more direct, compare the fields of the returned Product to specific (hard-coded) values in the test, again, without using the EntityGenerator in the test.
Q1: Don't make changes to code then write a test. Write a test first for the expected behavior. Then you can do whatever you want to the SUT.
Q2: You don't make the changes in your Product Gateway to change the owner of the product. You make the change in your model.
But if you insist, then listen to your tests. They are telling you that you have the possibility for products to be pulled from the gateway that have the incorrect owners. Oops, Looks like a business rule. Should be tested for in the model.
Also your using a mock. Why are you testing an implementation detail? The gateway only cares that the _productRepository.GetProduct(id) returns a product. Not what the product is.
If you test in this manner you will be creating fragile tests. What if product changes further. Now you have failing tests all over the place.
Your consumers of product (MODEL) are the only ones that care about the implementation of Product.
So your gateway test should look like this:
[Test]
public void GetProduct_return_the_same_product_as_getProduct_on_productRepository() {
var product = EntityGenerator.Product();
_productRepositoryMock.Setup(pr => pr.GetProduct(product.Id)).Returns(product);
_productService.GetProduct(product.Id);
_productRepositoryMock.VerifyAll();
}
Don't put business logic where it doesn't belong! And it's corollary is don't test for business logic where there should be none.
If you really want to guarantee that the service method doesn't change the attributes of your products, you have two options:
Define the expected product attributes in your test and assert that the resulting product matches these values. (This appears to be what you're doing now by cloning the object.)
Mock the product and specify expectations to verify that the service method does not change its attributes.
This is how I'd do the latter with NMock:
// If you're not a purist, go ahead and verify all the attributes in a single
// test - Get_Product_Does_Not_Modify_The_Product_Returned_By_The_Repository
[Test]
public Get_Product_Does_Not_Modify_Owner() {
Product mockProduct = mockery.NewMock<Product>(MockStyle.Transparent);
Stub.On(_productRepositoryMock)
.Method("GetProduct")
.Will(Return.Value(mockProduct);
Expect.Never
.On(mockProduct)
.SetProperty("Owner");
_productService.GetProduct(0);
mockery.VerifyAllExpectationsHaveBeenMet();
}
My previous answer stands, though it assumes the members of the Product class that you care about are public and virtual. This is not likely if the class is a POCO / DTO.
What you're looking for might be rephrased as a way to do comparison of the values (not instance) of the object.
One way to compare to see if they match when serialized. I did this recently for some code... Was replacing a long parameter list with a parameterized object. The code is crufty, I don't want to refactor it though as its going away soon anyhow. So I just do this serialization comparison as a quick way to see if they have the same value.
I wrote some utility functions... Assert2.IsSameValue(expected,actual) which functions like NUnit's Assert.AreEqual(), except it serializes via JSON before comparing. Likewise, It2.IsSameSerialized() can be used to describe parameters passed to mocked calls in a manner similar to Moq.It.Is().
public class Assert2
{
public static void IsSameValue(object expectedValue, object actualValue) {
JavaScriptSerializer serializer = new JavaScriptSerializer();
var expectedJSON = serializer.Serialize(expectedValue);
var actualJSON = serializer.Serialize(actualValue);
Assert.AreEqual(expectedJSON, actualJSON);
}
}
public static class It2
{
public static T IsSameSerialized<T>(T expectedRecord) {
JavaScriptSerializer serializer = new JavaScriptSerializer();
string expectedJSON = serializer.Serialize(expectedRecord);
return Match<T>.Create(delegate(T actual) {
string actualJSON = serializer.Serialize(actual);
return expectedJSON == actualJSON;
});
}
}
Well, one way is to pass around a mock of product rather than the actual product. Verify nothing to affect the product by making it strict. (I assume you are using Moq, it looks like you are)
[Test]
public void GetProduct_return_the_same_product_as_getProduct_on_productRepository() {
var product = new Mock<EntityGenerator.Product>(MockBehavior.Strict);
_productRepositoryMock.Setup(pr => pr.GetProduct(product.Id)).Returns(product);
Product returnedProduct = _productService.GetProduct(product.Id);
Assert.AreEqual(product, returnedProduct);
_productRepositoryMock.VerifyAll();
product.VerifyAll();
}
That said, I'm not sure you should be doing this. The test is doing to much, and might indicate there is another requirement somewhere. Find that requirement and create a second test. It might be that you just want to stop yourself from doing something stupid. I don't think that scales, because there are so many stupid things you can do. Trying to test each would take too long.
I'm not sure, if the unit test should care about "what given method does not". There are zillion steps which are possible. In strict the test "GetProduct(id) return the same product as getProduct(id) on productRepository" is correct with or without the line product.Owner = "totallyDifferentOwner".
However you can create a test (if is required) "GetProduct(id) return product with same content as getProduct(id) on productRepository" where you can create a (propably deep) clone of one product instance and then you should compare contents of the two objects (so no object.Equals or object.ReferenceEquals).
The unit tests are not guarantee for 100% bug free and correct behaviour.
You can return an interface to product instead of a concrete Product.
Such as
public IProduct GetProduct(int id)
{
return _productRepository.GetProduct(id);
}
And then verify the Owner property was not set:
Dep<IProduct>().AssertWasNotCalled(p => p.Owner = Arg.Is.Anything);
If you care about all the properties and or methods, then there is probably a pre-existing way with Rhino. Otherwise you can make an extension method that probably uses reflection such as:
Dep<IProduct>().AssertNoPropertyOrMethodWasCalled()
Our behaviour specifications are like so:
[Specification]
public class When_product_service_has_get_product_called_with_any_id
: ProductServiceSpecification
{
private int _productId;
private IProduct _actualProduct;
[It]
public void Should_return_the_expected_product()
{
this._actualProduct.Should().Be.EqualTo(Dep<IProduct>());
}
[It]
public void Should_not_have_the_product_modified()
{
Dep<IProduct>().AssertWasNotCalled(p => p.Owner = Arg<string>.Is.Anything);
// or write your own extension method:
// Dep<IProduct>().AssertNoPropertyOrMethodWasCalled();
}
public override void GivenThat()
{
var randomGenerator = new RandomGenerator();
this._productId = randomGenerator.Generate<int>();
Stub<IProductRepository, IProduct>(r => r.GetProduct(this._productId));
}
public override void WhenIRun()
{
this._actualProduct = Sut.GetProduct(this._productId);
}
}
Enjoy.
If all consumers of ProductService.GetProduct() expect the same result as if they had asked it from the ProductRepository, why don't they just call ProductRepository.GetProduct() itself ?
It seems you have an unwanted Middle Man here.
There's not much value added to ProductService.GetProduct(). Dump it and have the client objects call ProductRepository.GetProduct() directly. Put the error handling and logging into ProductRepository.GetProduct() or the consumer code (possibly via AOP).
No more Middle Man, no more discrepancy problem, no more need to test for that discrepancy.
Let me state the problem as I see it.
You have a method and a test method. The test method validates the original method.
You change the system under test by altering the data. What you want to see is that the same unit test fails.
So in effect you're creating a test that verifies that the data in the data source matches the data in your fetched object AFTER the service layer returns it. That probably falls under the class of "integration test."
You don't have many good options in this case. Ultimately, you want to know that every property is the same as some passed-in property value. So you're forced to test each property independently. You could do this with reflection, but that won't work well for nested collections.
I think the real question is: why test your service model for the correctness of your data layer, and why write code in your service model just to break the test? Are you concerned that you or other users might set objects to invalid states in your service layer? In that case you should change your contract so that the Product.Owner is readonly.
You'd be better off writing a test against your data layer to ensure that it fetches data correctly, then use unit tests to check the business logic in your service layer. If you're interested in more details about this approach reply in the comments.
Having look on all 4 hints provided it seems that you want to make an object immutable at runtime. C# language does no support that. It is possible only with refactoring the Product class itself. For refactoring you can take IReadonlyProduct approach and protect all setters from being called. This however still allows modification of elements of containers like List<> being returned by getters. ReadOnly collection won't help either. Only WPF lets you change immutability at runtime with Freezable class.
So I see the only proper way to make sure objects have same contents is by comparing them. Probably the easiest way would be to add [Serializable] attribute to all involved entities and do the serialization-with-comparison as suggested by Frank Schwieterman.

How do I Unit Test a function that inserts a record into a RIA Services DB?

This is a sample function that works with an entity, saves it to a db and then causes problems because we can't write a Unit Test for it. Check it out:
// this class exists in a Silverlight Class Library
public class EmployeeSaver
{
....
public void Go()
{
Employee e = new Employee();
e.Name="Jeremiah";
... // Other stuff that really needs to be tested
_DataContext.Employees.Add(e);
_DataContext.SubmitChanges();
}
}
Because the nature of RIA Services, a DomainService doesn't run inside of the Silverlight Unit Testing framework. This means I don't have access to RIA when I do my unit tests.
We've thought about mock databases, but this class actually creates an Entity (Employee) to be added to the DB. This is problematic because Mock Databases don't use this entity but a MockEntity class that looks similar to the original entity.
We're not trying to test RIA itself, but how we use the entities generated by RIA.
My end goal would be to write a function similar to this:
[TestMethod]
public void Test()
{
EmployeeSaver s = new EmployeeSaver();
s.Go();
Assert.IsEqual( DataContext.Employees.Last().Name, "Jeremiah" );
}
How can I test this function? What testing framework should I use? Am I way off for using the Silverlight Testing Framework?
In your unit test, instance s needs to have a stubbed out implementation of _DataContext. When the Go method is called, and it calls:
_DataContext.Employees.Add(e);
_DataContext.SubmitChanges();
it will call into your stub. The stub should then record the fact that an employee got added and changes were submitted.
After the call to Go, you should query the stub to ensure that the new employee got added, and call to SubmitChanges occurred.
As a secondary note:
I don't really agree with the last part of the other answer in that you should not care whether Go calls various methods of _DataContext. It is true that you're not concerned about testing _DataContext methods here, but the unit test for Go needs to ensure that the Go method is calling the _DataContext methods properly. The rationale is that every line of the Go method should be testable. If you didn't do this verification, then you could remove the calls to _DataContext method, breaking the code, but the unit test wouldn't catch it. This would break Bob Martin's the "three rules of TDD" principle.
A hand rolled mock database could store your object as is. We use such a system where the repositories are stored in dictionaries of .
You don't even need to go that far though. You could just use a mock interface for the _DataContext with something like RhinoMocks to make sure that the methods you expect to be called were (its not your concern for this test that _DataContext.SubmitChanges() works (that would be up it it's unit test) you only care that Go set the object and called save.

Why does my object mocking fail?

I am using the Moq and can't seem to get my unit test to pass on what appears to be a simple mocking scenario.
Product p = new Product();
var rep = new Mock<IProductRepository>();
rep.Expect(x => x.GetProductById(1)).Returns(p);
p = rep.Object.GetProductById(1);
Assert.AreEqual(1, p.ProductId); //Assert.AreEqual failed. Expected:<1>. Actual:<0>.
Any pointers on what I'm doing wrong? My Unit test reports:-
Assert.AreEqual failed. Expected:<1>. Actual:<0>.
I think you are missing the point of how to use mock objects in tests...
What you are doing is mocking a ProductRepository object while simultaneously testing it. That doesn't make much sense; you should not mock the object you're testing.
Let's say you have a class you want to test, ProductService, and it depends on another class, IProductRepository. When you test the ProductService, you will want to mock the dependency, IProductRepository. This allows you completely control the interaction between the class under test and its (mocked) dependency.
As you do so, your assertions will be based on what you expect the class under test, ProductService, to do. For instance, if you call the ProductService using something like productService.GetProductById(1), you will expect the ProductService object to call its IProductRepository method with the correct parameter exactly once: repository.GetProductById(1). You may also expect the ProductService to return the same object that the IProductRepository gave it. Regardless of what the repository does, that's the responsibility of the ProductService.
Having said that, your test may look something more like this:
//Arrange
int testId = 1;
var fakeProduct = new Product{ Id = testId };
var mockRepo = new Mock<IRepository>();
var productService = new ProductService(mockRepo);
mockRepo.Expect(repo => repo.GetProductById(testId)).Returns(fakeProduct);
//Act
Product returnedProduct = productService.GetProductById(testId);
//Assert
mockRepo.Verify(repo => repo.GetProductById(testId), TimesExactly(1));
Assert.AreEqual(returnedProduct.Id, fakeProduct.Id);
My syntax may be off, but hopefully the sample gets across a few points:
Don't mock the system under test
Mock the dependencies
Base your assertions on the responsibilities of the system under test, not the dependencies
You are creating an object, setting that object as the return value for a method, and then checking if the mock is altering the object, something that the mock is not intended to do You are basically doing this:
Product getProductById(Product p) { return p; }
...
Product p = new Product();
Assert.AreEqual(1, getProductById(p).ProductID );
when creating a new Product:
Product p = new Product();
i guess that the default ProductID is 0, so the sentence:
getProductById(p).ProductID
will obviously return 0.
I'm new to mock here too, but I don't see your point. What are you trying to test? The Product class, the ProductRepository, or interactions between them? That is the first thing to think about.
I'm looking at your code and it doesn't really look like you know what you're trying to achieve. Before writing any test, always ask the question: "What am I trying to prove, here?" The product you've created will be returned, but its ID will be the default value (0); this is expected behaviour, i.e. the mocking framework is working fine.
Test doubles (mocks, stubs, fakes, spies etc.) are used in tests to provide collaborators for the class under test. They feed interesting values into or receive calls from the class under test -- they are not the focus of the test.
Your test is currently asserting on a value returned by the test double itself. From what I can understand, either you're asking for help with an out of context code snippet (the snippet itself is just an example and not the test itself), or it is real test code that is pointless.
I've seen many a person tie themselves in knots with mocking frameworks. That's why, to begin with, I would recommend hand-writing your own test doubles. This helps you understand the interactions between the objects and form a clearer picture of what it is you wish to test. Again, this goes back to the question of "what am I trying to prove, here?".
Once you understand how to use hand-rolled test doubles to achieve a goal, you can graduate to mocking frameworks.
You're using the Product instance p once as 'the expected value' while you are setting up the expect - Returns (p)
and then using the same reference to store the return value of the actual call.
As for the Assert failure, does new Product() initialize its ProductId to 1. Seems like its being set to the default value of 0 - Hence the error.
I think the Mock framework is working.
I would second the view of Gishu re: the Product initialization. Unless the default behaviour of IProductRepository is to return a product referenced by the ProductId of '1', your test will fail.
And, may I add, this failure seems to be sensible behaviour. I think that you would like you ProductRepository to be empty upon initialization.
Not exactly missing the point, Mocking is not trivial.
In your case, you are having an IProductRepository that, I presume, is expected to hold Products. I assume that Products are not added to the ProductRepositort by default (per my earlier post), and I also assume that in order to reference a Product, it must have a productId (which by the way you really should mutate via a mutator).
If you would like to retrieve a Product from your ProductRepository, I think that you should add a Product to it through the mock framework (the moq site gives an example of registering and validating right at the top of the page), and ensure that the ProductRepostory either gives the Products added to it a default identifier (not recommended) or add an identifier to Product before adding it to the ProductRepository.

Categories

Resources