none
How to mock SetCommandTimeout and optionsBuilder using moq in .Net Core API RRS feed

  • Question

  • I need a help to mock and handle the SetCommandTimeout and optionsBuilder  for unit testing using Moq and NBuilder possibly?

    Database Context: 

    public class ImportContext : DbContext, IImportContext
    
    	{
    		public ImportContext()
    		{
    			Database.SetCommandTimeout(150000);
    		}
    		protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    		{
    			optionsBuilder.UseSqlServer(new WebServiceAppSettings().PConnectionString);
    		}
    		public DbContext GetContext(Type dataContextType)
    		{
    			return (DbContext)Activator.CreateInstance(dataContextType);
    		}
    		public DbSet<Data> Data { get; set; }
    		public DbSet<SerialNum> SerialNum { get; set; }
    
    	}



    Service: 

    public int GetSerialNo()
    		{
    			try
    			{
    				using (var context = new ImportContext())
    				{
    					var result = context.SerialNum.FirstOrDefault(p => p.SerialType == SerialNumType);
    					if (result != null)
    					{
    						var newHcpDataId = (Convert.ToInt32(result.SerialId) + 1);
    						result.SerialId = newDataId.ToString();
    						context.SaveChanges();
    						return newDataId;
    					}
    					return 0;
    				}
    			}
    			catch (Exception ex)
    			{
    				throw ex;
    			}
    		}




    Test:

    using System.Linq;
    using FizzWare.NBuilder;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Moq;
    using Services.Import.DBContext;
    using Services.Import.Entities;
    using Services.Import.Service;
    using Services.Import.Test.Helper;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore.Storage;
    
    namespace Services.Import.Test.Services
    {
        [TestClass]
        public class ImportServiceTest
        {
            private Mock<IEmailService> _mockEmailService;
            private Mock<IUtilityService> _mockUtilityService;
            private Mock<IImportContext> _mockImportContext;
            private ImportService _ImportService;
            private readonly RandomGenerator _random = new RandomGenerator();
            private readonly Mock<IImportContext> _mockContextFactory = new Mock<IImportContext>();
    
            [TestInitialize]
            public void TestSetup()
            {
                _mockImportContext = new Mock<IImportContext>();
                _mockEmailService= new Mock<IEmailService>();
                _mockUtilityService = new Mock<IUtilityService>();
                _ImportService = new ImportService(_mockImportContext.Object, _mockUtilityService.Object, _mockEmailService.Object);
            }
       
            [TestMethod]
            public void GetSerialNo_WhenItCalled_ShouldFindSerialNo()
            {
                // Arrange
                var expectedResult =  _random.Int();
                var serialNum = Builder<SerialNum>.CreateListOfSize(5).All().Build();
                _mockImportContext.Setup(mc => mc.SerialNum).Returns(ContextExtension.GetQueryableMockDbSet(serialNum.ToList()));
    
                //Mock<Database> mockdatabase= new Mock<Database>("", _mockImportContext.Object);
    
                // Act
                var actualResult = _ImportService.GetSerialNo();
    
                // Assert
                Assert.AreEqual(expectedResult, actualResult);
            }
        }
    }



    When i execute the test for the case GetSerialNo_WhenItCalled_ShouldFindSerialNo it fails as it is missing the environment for SetCommandTimeout and optionsBuilder. When i comment those two my test is working fine.

    Teaching me on how to mock the above test without any code changes in the DBContext will be really grateful. 

    Thank you for your support in advance. 

     




    Friday, November 22, 2019 10:32 AM

All replies

  • Myself, I would just do an integration test. I wouldn't unit test the code. If doing a unit test, I would mock out a method GetSerialNo() was using that had the try/catch and EF code in it and just returned  an Arranged Int value out of the mock.

    public int GetSerialNo()
    {
           int serialno = 0;
           serialno =  GetTheNumber();     // this would be mocked out and the Arranged Int return.
          return serialno;
    }


    Friday, November 22, 2019 12:54 PM
  • But How will you bypass the context ? I am an expert but i believe we have mock that too.

    SomaSundaram R

    Monday, November 25, 2019 5:00 AM
  • But How will you bypass the context ? I am an expert but i believe we have mock that too.

    SomaSundaram R

    You mock out GetTheNumber() in the unit test and arrange for an int to be retuned out of the mock for the GetTheNumber() so that the 'actual' for GetSerialNo() returns the arranged int from the mock.

    You can Assert that the arranged int from the mock of GetTheNumber() was returned,  and Assert that the mock for GetTheNumber() was executed.

    Of course, GetTheNumber() is never executed, becuase it is mocked out and therefor a context is not in play due to the code for the context is never executed due to the GetTheNumber() is never executed.

    IMHO, if you want to test GetSerialNo() with your actual code as is, then do an integration test on the real code as is and forget about unit testing it.

    Or you refactor the code as I have explained so that you can do an unit test and an intergration test

    GettheNumber() method should be in its own class that implements an interface so that it can be dependency injected and also so that GettheNumber() can be mocked out in the UT.

    You can try the EF forum for further help.

    https://social.msdn.microsoft.com/Forums/en-US/home?forum=adodotnetentityframework

    public int GetSerialNo()
    {
           int serialno = 0;
           serialno =  GetTheNumber();     // this would be mocked out and the Arranged Int return.
          return serialno;
    }

    public int GetTheNumber()
    		{
    			try
    			{
    				using (var context = new ImportContext())
    				{
    					var result = context.SerialNum.FirstOrDefault(p => p.SerialType == SerialNumType);
    					if (result != null)
    					{
    						var newHcpDataId = (Convert.ToInt32(result.SerialId) + 1);
    						result.SerialId = newDataId.ToString();
    						context.SaveChanges();
    						return newDataId;
    					}
    					return 0;
    				}
    			}
    			catch (Exception ex)
    			{
    				throw ex;
    			}
    		}


    • Edited by DA924x Monday, November 25, 2019 7:19 AM
    Monday, November 25, 2019 7:17 AM
  • Agree with your points. But can you please let me How to mock SetCommandTimeout and optionsBuilder using moq? i am more interested to learn it. 

    SomaSundaram R

    Tuesday, November 26, 2019 6:28 AM
  • Agree with your points. But can you please let me How to mock SetCommandTimeout and optionsBuilder using moq? i am more interested to learn it. 

    SomaSundaram R

    IMHO, trying to unit test code using EF is a waste of time. I have written many an integration tests using EF.  You can check with Stackoverflow,  MSDN, and ASP.NET forums for EF with ASP.NET being at http://forums.asp.net/ and see if you can be helped.

    The only way I would do a test with EF Core that is not an integration test against a database out on a database server like MS SQL Server is to use an in-memory database. 

    https://docs.microsoft.com/en-us/ef/core/miscellaneous/testing/in-memory

    • Edited by DA924x Tuesday, November 26, 2019 7:03 AM
    Tuesday, November 26, 2019 6:56 AM