chevron-thin-right chevron-thin-left brand cancel-circle search youtube-icon google-plus-icon linkedin-icon facebook-icon twitter-icon toolbox download check linkedin phone twitter-old google-plus facebook profile-male chat calendar profile-male
Welcome to Typemock Community! Here you can ask and receive answers from other community members. If you liked or disliked an answer or thread: react with an up- or downvote.
0 votes
Hi,

I have a test that requires the return value to be an object with properties set. I have done this using recorder.Return("Test"); but I' not able to get this to work with an object for example recorder.Return(new testobject("Some test data"));

In the example code I have pasted at the bottom testClass is created and should call GetProduct

I would like this to return an object with the values of the Product object that was set up in the using section.

The test fails with
Assert.IsTrue failed. Product description does not match

Thanks for you help

*********************************************

Here's the unit test I've written

*********************************************
        [TestMethod]
        [VerifyMocks]
        public void MockTest()
        {
            DatabaseTest.BizProduct testClass = new DatabaseTest.BizProduct();


            using (RecordExpectations recorder = RecorderManager.StartRecording())
            {
                DatabaseTest.Product Product = new DatabaseTest.Product();
                Product.Description = PRODUCT_DESCRIPTION;
                Product.Price = PRODUCT_PRICE;
                Product.ProductCode = PRODUCT_CODE;

                DatabaseTest.ProductAccess dbAccess = new DatabaseTest.ProductAccess();
                recorder.ExpectAndReturn(dbAccess.GetProduct(null), Product);
                
            }

           DatabaseTest.Product product =  testClass.GetProduct(PRODUCT_CODE);
            Assert.IsTrue(product.Description == PRODUCT_DESCRIPTION, "Product description does not match");
            Assert.IsTrue(product.Price == PRODUCT_PRICE, "Product price is incorrect");
            Assert.IsTrue(product.ProductCode == PRODUCT_CODE, "Product code is incorrect");
        }
asked by tacky (720 points)

10 Answers

0 votes
Hi
inside the recording block all the assignments are just expectations you record thus:
Product.Description = PRODUCT_DESCRIPTION does not set any field
inside the Product but creates an expectation for the set property Description. So what you want to do here is call the original properties.

I think that what you are trying here will be more clear using the
Arrange Act Assert API so I'll give you two examples
The first using the natural mocks as you did and the second will use
Arrange Act Assert API.

Natural mocks:
public void MockTest() 
{
   DatabaseTest.BizProduct testClass = new DatabaseTest.BizProduct(); 

   using (RecordExpectations recorder = RecorderManager.StartRecording())
   {
      DatabaseTest.Product Product = new DatabaseTest.Product();

      //This will stop the mocking for this block untill we call RecorderManager.StartRecording() again.
      //You can also use recorder.CallOriginal() after each call to a property.
      RecorderManager.StopRecording();
      Product.Description = PRODUCT_DESCRIPTION;
      Product.Price = PRODUCT_PRICE;
      Product.ProductCode = PRODUCT_CODE;
      RecorderManager.StartRecording();

      DatabaseTest.ProductAccess dbAccess = new DatabaseTest.ProductAccess();
      recorder.ExpectAndReturn(dbAccess.GetProduct(null), Product); 
   }

   DatabaseTest.Product product = testClass.GetProduct(PRODUCT_CODE);
   Assert.IsTrue(product.Description == PRODUCT_DESCRIPTION, "Product description does not match");
   Assert.IsTrue(product.Price == PRODUCT_PRICE, "Product price is incorrect");
   Assert.IsTrue(product.ProductCode == PRODUCT_CODE, "Product code is incorrect"); 
}


Arrange Act Assert example:
public void MockTest()
{
   //create a fake instance of Product. We set the default to call original members. 
   DatabaseTest.Product fakeProduct = Isolate.Fake.Instance<DatabaseTest>(Members.CallOriginal);
   
   //setting the fields. in this case the properties get called 
   //because we set the default to Members.CallOriginal
   fakeProduct.Description = PRODUCT_DESCRIPTION;
   fakeProduct.Price = PRODUCT_PRICE;
   fakeProduct.ProductCode = PRODUCT_CODE;
   
   //swap the our fake with the next created instance of Product class.
   Isolate.SwapNextInstance<DatabaseTest>().With(fakeProduct);

   //create a fake instance of ProductAccess.
   DatabaseTest.ProductAccess fakeProductAccess = Isolate.Fake.Instance<DatabaseTest>();
   Isolate.WhenCalled(() => fakeProductAccess.GetProduct(null)).WillReturn(fakeProduct);
   //swap the our fake with the next created instance of ProductAccess class.
   Isolate.SwapNextInstance<DatabaseTest>().With(fakeProductAccess);

   DatabaseTest.BizProduct testClass = new DatabaseTest.BizProduct();

   DatabaseTest.Product product = testClass.GetProduct(PRODUCT_CODE);
   Assert.IsTrue(product.Description == PRODUCT_DESCRIPTION, "Product description does not match");
   Assert.IsTrue(product.Price == PRODUCT_PRICE, "Product price is incorrect");
   Assert.IsTrue(product.ProductCode == PRODUCT_CODE, "Product code is incorrect"); 
}


:arrow: Check out our blog for tutorials on the Arrange Act Assert API.
answered by ohad (35.4k points)
0 votes
Hi ohad,

Thanks for your reply. I'm afraid that my test is still failing, the test details are still not being picked up.

I'm unable to use the Arrange Act Assert method as we are using .NET 2.0

I have tried your two other suggestions. Could you please take a look and see if I'm doing something wrong here.

Thanks.

        [TestMethod]
        [VerifyMocks]
        public void MockTestCallOriginalTest()
        {
            DatabaseTest.BizProduct testClass = new DatabaseTest.BizProduct();


            using (RecordExpectations recorder = RecorderManager.StartRecording())
            {
                DatabaseTest.Product Product = new DatabaseTest.Product();
                Product.Description = PRODUCT_DESCRIPTION;
                recorder.CallOriginal();
                Product.Price = PRODUCT_PRICE;
                recorder.CallOriginal();
                Product.ProductCode = PRODUCT_CODE;
                recorder.CallOriginal();


                DatabaseTest.ProductAccess dbAccess = new DatabaseTest.ProductAccess();
                recorder.ExpectAndReturn(dbAccess.GetProduct(null), Product);

            }
           
            DatabaseTest.IProduct product = testClass.GetProduct(PRODUCT_CODE);
            Assert.IsTrue(product.Description == PRODUCT_DESCRIPTION, "Product description does not match");
            Assert.IsTrue(product.Price == PRODUCT_PRICE, "Product price is incorrect");
            Assert.IsTrue(product.ProductCode == PRODUCT_CODE, "Product code is incorrect");
        }


[/code]
answered by tacky (720 points)
0 votes
You should seriously consider using the RecorderManager.StopRecording() and RecorderManager.StartRecording() and set the properties in between, just like ohad is doing.

The first block of code he has shown you isn't AAA code, it's 'regular' natural mocks code.
answered by dvdstelt (5.3k points)
0 votes
Thanks again for your reply. All help is greatly appreciated.

I did try the stop recording method but didn't have any effect either.

Would it be any help for me to post up the DatabaseTest.Product class code? There's not much in there it just just has properties that set private variables.

Here's the code I used for the start stop recorder test

        [TestMethod]
        [VerifyMocks]
        public void MockTestStopRecordingTest()
        {
            DatabaseTest.BizProduct testClass = new DatabaseTest.BizProduct();


            using (RecordExpectations recorder = RecorderManager.StartRecording())
            {
                DatabaseTest.Product Product = new DatabaseTest.Product();
                RecorderManager.StopRecording();
                Product.Description = PRODUCT_DESCRIPTION;
                Product.Price = PRODUCT_PRICE;
                Product.ProductCode = PRODUCT_CODE;
                RecorderManager.StartRecording();

                DatabaseTest.ProductAccess dbAccess = new DatabaseTest.ProductAccess();
                recorder.ExpectAndReturn(dbAccess.GetProduct2(null), Product);

            }

            // DatabaseTest.IProduct product =  testClass.GetProduct(PRODUCT_CODE);
            DatabaseTest.IProduct product = testClass.GetProduct(PRODUCT_CODE);
            Assert.IsTrue(product.Description == PRODUCT_DESCRIPTION, "Product description does not match");
            Assert.IsTrue(product.Price == PRODUCT_PRICE, "Product price is incorrect");
            Assert.IsTrue(product.ProductCode == PRODUCT_CODE, "Product code is incorrect");
        }
answered by tacky (720 points)
0 votes
I would really advise to use the AAA - it is possible to have the production code in .NET 2.0 and the tests in .NET 3.5. as Visual Studio supports multi-versions.

There is a big difference when using the new API's. Using the new API's we think in terms of 'This is not really important to my tests - lets fake it'

This will make our test simple with less density and more robust to code changes.
This is how the test will look: (We don't care how the ProductAccess works we just want to get a specific product)

public void MockTest()
{
   //create a fake hierarchy of ProductAccess.
   ProductAccess fakeProductAccess =
     Isolate.Fake.Instance<ProductAccess>(Members.ReturnRecursiveFakes);
   Isolate.SwapNextInstance<ProductAccess>().With(fakeProductAccess);

   // grab the fake product and set it up
   DatabaseTest.Product fakeProduct = fakeProductAccess.GetProduct(PRODUCT_CODE);
     fakeProduct.Description = PRODUCT_DESCRIPTION;
     fakeProduct.Price = PRODUCT_PRICE;
     fakeProduct.ProductCode = PRODUCT_CODE;
   

   BizProduct testClass = new BizProduct();
   Product product = testClass.GetProduct(PRODUCT_CODE);

   Assert.IsSame(fakeProduct,product);
} 
answered by eli (5.7k points)
0 votes
This is what I've got
      [TestMethod]
      [VerifyMocks]
      public void MyTest()
      {
         BizProduct testClass = new BizProduct();
         Product tempProduct = new Product();

         using (RecordExpectations recorder = RecorderManager.StartRecording())
         {
            RecorderManager.StopRecording();
            tempProduct.Price = 5;
            tempProduct.Description = "Fake description";
            RecorderManager.StartRecording();

            ProductAccess fakeAccess = new ProductAccess();
            recorder.ExpectAndReturn(fakeAccess.GetProduct2(0), tempProduct);
         }

         Product product = testClass.GetProduct(10);

         Assert.AreEqual(5, product.Price);
         Assert.AreEqual("Fake description", product.Description);
      }


   }

   public class BizProduct
   {
      public Product GetProduct(int productCode)
      {
         ProductAccess productAccess = new ProductAccess();
         return productAccess.GetProduct2(productCode);
      }
   }

   public class ProductAccess
   {
      public Product GetProduct2(int productCode)
      {
         Product product = new Product();
         product.ProductCode = productCode;
         product.Price = 10;
         product.Description = "Real description";

         return product;
      }
   }

   public class Product
   {
      public string Description { get; set; }
      public int Price { get; set; }
      public int ProductCode { get; set; }
   }


As you can see I've taken the creation of the Product outside of the recording block or else VerifyMocks would fail. Because when you set recorder.ExpectAndReturn() the "real" creation of the Product isn't happening. Instead it's immediately returning the created tempProduct.

Perhaps you could show a bit more code?
answered by dvdstelt (5.3k points)
0 votes
Eli, most people who are still using .NET 2.0 definitely don't have access to VS2008. It is a cool idea though to use VS2008 and writing tests in .NET 3.5 and production code in .NET 2.0.

Although the only reason not to upgrade to .NET 3.5 and VS2008 is the licenses you need for VS2008. There's no reason not to upgrade from .NET 2.0 to .NET 3.5 because the CLR remains the same.
answered by dvdstelt (5.3k points)
0 votes
True Dennis,
You can still use the AAA with .NET 2.0 (you just have to add core.dll)

Actually, the example could be simpler (without using Recorder.StopRecording)

[TestMethod,VerifyMocks]
public void MyTest()
{
    BizProduct testClass = new BizProduct();
    Product tempProduct = new Product();
    tempProduct.Price = 5;
    tempProduct.Description = "Fake description";

    using (RecordExpectations recorder = RecorderManager.StartRecording())
    {
        ProductAccess fakeAccess = new ProductAccess();
        recorder.ExpectAndReturn(fakeAccess.GetProduct2(0), tempProduct).RepeatAlways();
    }

    Product product = testClass.GetProduct(10);

    Assert.AreEqual(5, product.Price);
    Assert.AreEqual("Fake description", product.Description);
} 
answered by eli (5.7k points)
0 votes
True Dennis,
You can still use the AAA with .NET 2.0 (you just have to add core.dll)

System.Core.dll? Hmmm, never tried that. But should probably work indeed.

Actually, the example could be simpler (without using Recorder.StopRecording)

Right, I was to fixated on the stop and start. Thanks.
answered by dvdstelt (5.3k points)
0 votes
Hi Eli,

My first crack at writing the unit test created the product object outside the recorder block as you have shown. That make sense to me as you don't want it mocked.

I have however tracked down my problem with the unit test. My test class (DatabaseTest.BizProduct) has a constructor that creates the database access object (DatabaseTest.ProductAccess).

The problem here is that I have created the test class before the recorder block. The mocked database access object is not being used as the testclass has already created its data access object.

So if you mock an object that is created in your test class constructor be careful to create you test class after you have completed your recording block.

I retested the two previous suggestions moving the test class creation to after the recording block.
See "MockTestStopRecordingTest" and "MockTestCallOriginalTest"

MockTestStopRecordingTest, works
MockTestCallOriginalTest, still fails. It appears that the values plugged in are not being used and the plain uninitialised object.

Thanks for all your help

Below is the code for the test class showing the constructor creating the data access object.


    public class BizProduct 
    {
        private IProductDataAccess _dbAccess = null;

        public BizProduct()
        {
            _dbAccess = new ProductAccess();
        }

        public BizProduct(IProductDataAccess DBaccess)
        {
            _dbAccess = DBaccess;
        }

        public IProduct GetProduct(string ProductCode)
        {
            return _dbAccess.GetProduct(ProductCode);          
        }
}
answered by tacky (720 points)
...