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 am trying to mock the call to the SaveChanges method on a Entity Framework data context object.

Here is the method to test:

      public static Account AddNewAccount(Account newAccount)
      {
         if (newAccount.ID == 0)
            throw new AccountCreationException(ReturnCodes.AccountIDAlreadyExists, null, newAccount);

         try
         {
            using (CoreAccountsEntities db = new CoreAccountsEntities())
            {
               db.AddToAccount(newAccount);
               db.SaveChanges();
               return newAccount;
            }
         }
         catch (Exception ex)
         {
            throw new AccountCreationException(ReturnCodes.GeneralError, ex, newAccount);
         }
      }


In this method, I want to mock the db.SaveChanges, and the return of the newAccount now populated with a new ID. Here is the test I have made so far:

      [TestMethod]
      [VerifyMocks]
      public void AddAccountSavesNewAccountToDatabaseAndReturnsNewAccountWithAccountID()
      {
         Account newAccount = new Account();
         newAccount.AccountName = "New unit test account";
         newAccount.CannotHaveInternalTransactions = true;
         newAccount.CanOverdraft = false;
         newAccount.CanReceive = true;
         newAccount.CanReceiveBankTransactions = false;
         newAccount.CanReceiveCreditCardTransactions = false;
         newAccount.CanSend = true;
         newAccount.Currency = "EUR";
         newAccount.LastUpdated = DateTime.Now;
         newAccount.SupportsSMSBlock = false;

         Account savedAccount;
         savedAccount.ID = 1;
         savedAccount.AccountName = "New unit test account";
         savedAccount.CannotHaveInternalTransactions = true;
         newAcsavedAccountcount.CanOverdraft = false;
         savedAccount.CanReceive = true;
         savedAccount.CanReceiveBankTransactions = false;
         savedAccount.CanReceiveCreditCardTransactions = false;
         savedAccount.CanSend = true;
         savedAccount.Currency = "EUR";
         savedAccount.LastUpdated = DateTime.Now;
         savedAccount.SupportsSMSBlock = false;

         using (RecordExpectations recorder = RecorderManager.StartRecording())
            {
            CoreAccountsEntities db = new CoreAccountsEntities();
            db.  <== This is where the SaveChanges() method don't appear...
            }

         newAccount.ID = 1;
         Assert.AreEqual<Account>(newAccount, savedAccount);
      }


I have checked to see that the SaveChanges method is in fact public, so I am stuck. Please be kind as I am a total newbie with TypeMock... (and mocking in general)

Morten
asked by petteroe (3k points)

7 Answers

0 votes
Hi Morten
Welcome to the forum 8)
I assume that the test code is in separate assembly than the code under test.
First check if the test project has a reference to the Entity framework assembly (The same as the production code)
Than check if include the correct using for the namespace at the head of the code file.
Please tell me if it helps.
answered by ohad (35.4k points)
0 votes
Thanks Ohad,

I was so caught up in the new mocking stuff that I overlooked the basics. It turned out to be just a missing reference to the System.Data.Entity assembly.
answered by petteroe (3k points)
0 votes
I have a follow up question:

Using the same method and test as above, I need to have the test allow the CoreAccountsEntities actually be created (because it will load the mapping stuff and this is probably too much to mock), but also intercept the call to SaveChanges.

I tried to put this in the recorder using block:

CoreAccountsEntities db = new CoreAccountEntities();
db.SaveChanges();
recorder.Return(1);


but this throws a runtime error.

I then tried to pull the instantiation out of the using block like this:

CoreAccountsEntities db = new CoreAccountEtities();
using (RecordExpectations recorder = RecorderManager.StartRecording())
{
    db.SaveChanges();
    recorder.Return(1);
}


This fails with a message from TypeMock that one more call to SaveChanges() is expected, and also the data was saved to the database so the call was obviously not intercepted. I have no problem understanding why, because there is no way for the recorder to now which instance to call the SaveChanges method on. But how do I go about doing this?

Thanks,

Morten
answered by petteroe (3k points)
0 votes
Hi
Can you please post the error message you're getting in your first example?
i.e. when you do this:
CoreAccountsEntities db = new CoreAccountEntities();
db.SaveChanges();
recorder.Return(1); 


Please tell me what line throws the runtime error.
answered by ohad (35.4k points)
0 votes
The error occurs when I hit the db.AddToAccount(newAccount) line in the production code above.

Here is the error message:

Test method [...snipped namespace...].AccountOperationsTests.AddAccountSavesNewAccountToDatabaseAndReturnsNewAccountWithAccountID threw exception:  [...snipped namespace...].AccountCreationException: GeneralError --->  System.ArgumentNullException: Value cannot be null.
Parameter name: metadataWorkspace.


I am assuming that this error occurs because the mocked instance of the entity context has not been initialized with the proper mappings.


Thanks,

Morten
answered by petteroe (3k points)
0 votes
Hi Mortan
My guess is that since the constructor of CoreAccountEntities is mocked some
field in the class was not initiated properly and this is the cause to the exception.
By default the constructor of CoreAccountEntities is mocked. If you want to change that use recorder.CallOriginal after the call to the constructor.
CoreAccountsEntities db = new CoreAccountEntities(); 
recorder.CallOriginal();


Please tell me if it works.
answered by ohad (35.4k points)
0 votes
Thanks a lot Ohad,

That did the trick. I was looking for a "Use real constructor" type metod to instantiate the datacontext, but didn't give the CallOriginal behaviour credit for all that it can do... Great stuff.

The small downside to my specific scenario now is that I am relying on real mapping files for the entity framework being sucked into the mock. This is because I haven't found an easy way to isolate only the mappings needed for each test, and frankly I think that would have created a little too much work. So I am relying on a tiny bit of external configuration in my unit tests... So what! :wink:
answered by petteroe (3k points)
...