3

I'm using xUnit, NSubstitute, FluentAssertions and Sitecore FakeDb. Here is the controller that needs a test,

public class TransactionsController : Controller
    {
        private readonly IRenderingContext _renderingContext;
        private readonly IProductService _service;
        private User CurrentUser { get { return Sitecore.Context.User; } }

        public TransactionsController(IRenderingContext renderingContext, IProductService service)
        {
            Assert.ArgumentNotNull(renderingContext, nameof(renderingContext));
            Assert.ArgumentNotNull(service, nameof(service));

            _renderingContext = renderingContext;
            _service = service;
        }

        public ActionResult Transactions()
        {
            var productParam = new GetProductsParameters
            {
                ClientId = CurrentUser.Name,
                NextRecordNumber = 0,
                NumberOfRowsToReturn = 100
            };
            var clientId = CurrentUser.Name;

            TransactionsViewModel viewModel = new TransactionsViewModel
            {
                Item = _renderingContext.RenderingDatasource as ITransactionsItem,
                AccountsList = new List<SomeList>(),
                ProductsList = _service.GetPreviousProucts(productParam).PreviousProducts ?? new List<PreviousProduct>()
            };
            viewModel.ProductsList.OrderBy(c => c.ProductName);

            var innotrustIds = CurrentUser.Profile.GetCustomProperty("Innovue ID").Split(',');
            foreach (var id in innotrustIds)
            {
                var acctParam = new GetOtherProductParameters
                {
                    InnovueuserId = CurrentUser.Profile.GetCustomProperty("Innovue ID")
                };
                var response = _service.GetOtherProducts(acctParam).OtherProducts;
                if (response != null)
                    viewModel.AccountsList = viewModel.AccountsList.Concat(response);
            }

            if (viewModel.Item == null) return new NullModelResult(_renderingContext, RouteData);

            return View(@"/Path/To/Transactions.cshtml", viewModel);
        }
    }

And the following unit test is pretty much trying to test whether the above controller returns a view

    [Fact]
    public void Transactions_Returns_View()
    {
        using (var db = new Db())
        {
            var _productService = Substitute.For<IProductService>();

            var _renderingContext = Substitute.For<IRenderingContext>();

            var product1 = new PreviousProduct
            {
                ContactName = "Name",
                ContactAddressLine1 = "Address",
                ContactId = "123909",
                ContactCity = "SomeCity",
                ContactState = "State",
                ContatctZipCode = "73456",
                ContactComment = "Comment"
            };

            var product2 = new PreviousProduct
            {
                ContactName = "Name2",
                ContactAddressLine1 = "Address2",
                ContactId = "1239092",
                ContactCity = "SomeCity2",
                ContactState = "State",
                ContatctZipCode = "73456",
                ContactComment = "Comment2"
            };

            var productsList = new List<PreviousProduct>();
            productsList.Add(product1);
            productsList.Add(product2);

            _productService.GetPreviousProducts(Arg.Any<GetPreviousProductsParameters>()).PreviousProducts.ReturnsForAnyArgs(productsList);

            var sut = new TransactionsController(_renderingContext, _productService);

            var result = sut.Transactions();

            result.Should().BeOfType<ViewResult>();
        }
    }

Test fails executing _productService.GetPreviousProducts mock service returning

System.NullReferenceException: 'Object reference not set to an instance of an object.'

Feature.Services.Services.IProdcutService.GetPreviousProducts(...) returned null.

ReturnsForAnyArgs injects some sample data productsList. The underlying IProdcutService.GetPreviousProducts method calls another REST service to get a list of PreviousProducts from a 3rd party web service. Any insight into why the test fails with NullReferenceException without using ReturnsForAnyArgs(productsList)? What am I missing in this framework?

Note: Installed and using Sitecore.FakeDb because one of the previous exception was about 'Sitecore.Context.User' and I understand that this needs to be isolated to make the controllers more unit test friendlier down the road. Using FakeDb but no items are created in the test at this point. We're planning on using AutoFixture to auto mock data in tests once the basic tests work. We're on Sitecore 9.0.2 for reference.

1 Answer 1

3

By mocking a proper return type(GetPreviousProductsResponse) value for the injected service(_productService) instead of a model property(PreviousProducts) solved this issue.

_productService.GetPreviousProducts(Arg.Any<GetPreviousProductsParameters>()).Returns(new GetPreviousProductsResponse);

The test still doesn't return 'ViewResult' type as intended because 'viewModel.item' in my controller is null and returns 'NullModelResult' type. But, that's something can be handled when I clean up unit test 'Arrange' section with sample data using AutoFixture. Time to focus on some real logic in other controllers!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.