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.