This is the method that I want to test:
public async Task<object> CreateExpense(CreateExpenseCommand command)
{
var validationResults = await _validator.ValidateAsync(command);
if (!validationResults.IsValid)
{
return validationResults.Errors.First().ToString();
}
//more code that is irrelevant for this post
}
To test this, I need to mock _validatior, which is defined as private readonly IValidator<CreateExpenseCommand> _validator; and is used through constructor injection.
I am using AutoFixture with AutoMoqCustomizations and Moq for mocking. Maybe I should use Moq exclusively?
This is what I try to do in the test:
[Fact]
public async Task CreateExpense_Success()
{
//Arrange
var service = _fixture.Fixture.Freeze<Mock<IValidator<CreateExpenseCommand>>>();
service.Setup(x => x.Validate((CreateExpenseCommand)It.IsAny<IValidator<CreateExpenseCommand>>())).Returns(It.IsAny<ValidationResult>);
//more code that is irrelevant for this post
}
However, this results in error:
System.NullReferenceException: 'Object reference not set to instance of an object'.
The error is pretty self-explanatory, but I don't know how to mock correctly.
Image of error:
You need to return an object when doing the setup:
service.Setup(x => x.Validate(It.IsAny<IValidator<CreateExpenseCommand>>()))
.Returns(<*1>);
*1 - Here return the object that you wish to be returned when you call ValidateAsync function. Don't do It.IsAny as it returns null which causes the NullReferenceException.
Also, you need to add virtual to the ValidateAsync method in order to let it be overridable.
Related
I have a controller with the following signature:
public CustomerTypeController(
IHttpContextAccessor accessor,
IPrincipalProvider provider,
IMapper mapper,
ILogger<CustomerTypeController> logger,
ICustomerTypeService customerTypeService)
{ }
For now my Theory looks like this:
[Theory, AutoMoqData]
public void GetWhenHasCustomerTypesShouldReturnOneCustomerType(
IFixture fixture,
[Frozen] Mock<ICustomerTypeService> service,
CustomerTypeController sut)
{
//Arrange
var items = fixture.CreateMany<Model.CustomerType>(3).ToList();
//Act
var result = sut.Get(1);
//Assert
Assert.IsType<OkResult>(result);
}
When I run this test as-is, I get the following exception:
AutoFixture.ObjectCreationExceptionWithPath : AutoFixture was unable to create an instance from Microsoft.AspNetCore.Mvc.ModelBinding.BindingInfo because creation unexpectedly failed with exception. Please refer to the inner exception to investigate the root cause of the failure.
Inner exception messages:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
System.ArgumentException: The type 'System.Object' must implement 'Microsoft.AspNetCore.Mvc.ModelBinding.IModelBinder' to be used as a model binder. (Parameter 'value')
What am I doing wrong, and how do I solve the problem?
TL;DR
You should decorate the controller parameter in your test method with the [NoAutoProperties] attribute.
[Theory, AutoMoqData]
public void GetWhenHasCustomerTypesShouldReturnOneCustomerType(
IFixture fixture,
[Frozen] Mock<ICustomerTypeService> service,
[NoAutoProperties] CustomerTypeController sut)
{
//Arrange
var items = fixture.CreateMany<Model.CustomerType>(3).ToList();
//Act
var result = sut.Get(1);
//Assert
Assert.IsType<OkResult>(result);
}
Update
Now that I know the AutoFixture code-base a little better, I wanted to understand why does this actually fix the issue.
The Greedy attribute normally instructs AutoFixture to use the constructor with the largest number of parameters, which should have nothing to do with the fix.
As the error message states, the exception occurs when a property is being set and the property is expecting a value that implements IModelBinder. The origin of the error is the BinderType property of the BindingInfo class, which is of type System.Type. By default AutoFixture will resolve Type as System.Object, which explains the error message.
When the Greedy attribute is applied, this customizes AutoFixture to create an instance of the property type, using a custom factory. The resulting builder graph node, (likely by accident) skips setting any properties, on the created instance.
Taking this into consideration, a more fitting resolution should be a using the NoAutoProperties attribute. This will explicitly instruct AutoFixture to ignore all auto-properties in the decorated type, but will leave the constructor query as "modest".
Since adding the attribute everywhere might get annoying and tedious, I suggest customizing AutoFixture to ignore all properties from ControllerBase, in the domain customization. Also in case you're using property injection, this will allow AutoFixture to instantiate the controller properties.
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute()
: base(() => new Fixture().Customize(
new CompositeCustomization(
new AutoMoqCustomization(),
new AspNetCustomization())))
{
}
}
public class AspNetCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new ControllerBasePropertyOmitter());
}
}
public class ControllerBasePropertyOmitter : Omitter
{
public ControllerBasePropertyOmitter()
: base(new OrRequestSpecification(GetPropertySpecifications()))
{
}
private static IEnumerable<IRequestSpecification> GetPropertySpecifications()
{
return typeof(ControllerBase).GetProperties().Where(x => x.CanWrite)
.Select(x => new PropertySpecification(x.PropertyType, x.Name));
}
}
If you need the properties in ControllerBase for some reason then just instruct AutoFixture how to properly create BindingInfo instances.
Original answer
You should decorate the controller parameter in your test method with the [Greedy] attribute.
[Theory, AutoMoqData]
public void GetWhenHasCustomerTypesShouldReturnOneCustomerType(
IFixture fixture,
[Frozen] Mock<ICustomerTypeService> service,
[Greedy] CustomerTypeController sut)
{
//Arrange
var items = fixture.CreateMany<Model.CustomerType>(3).ToList();
//Act
var result = sut.Get(1);
//Assert
Assert.IsType<OkResult>(result);
}
We have a controller that derives from ControllerBase with an action like this:
public async Task<ActionResult> Get(int id)
{
try
{
// Logic
return Ok(someReturnValue);
}
catch
{
return Problem();
}
}
We also have a unit test like this:
[TestMethod]
public async Task GetCallsProblemOnInvalidId()
{
var result = sut.Get(someInvalidId);
}
But ControllerBase.Problem() throws a Null Reference Exception. This is a method from the Core MVC framework, so I don't realy know why it is throwing the error. I think it may be because HttpContext is null, but I'm not sure.
Is there a standardized way to test a test case where the controller should call Problem()?
Any help is appreciated.
If the answer involves mocking: we use Moq and AutoFixtrue.
The null exception is because of a missing ProblemDetailsFactory
In this case the controller needs to be able to create ProblemDetails instance via
[NonAction]
public virtual ObjectResult Problem(
string detail = null,
string instance = null,
int? statusCode = null,
string title = null,
string type = null)
{
var problemDetails = ProblemDetailsFactory.CreateProblemDetails(
HttpContext,
statusCode: statusCode ?? 500,
title: title,
type: type,
detail: detail,
instance: instance);
return new ObjectResult(problemDetails)
{
StatusCode = problemDetails.Status
};
}
Source
ProblemDetailsFactory is a settable property
public ProblemDetailsFactory ProblemDetailsFactory
{
get
{
if (_problemDetailsFactory == null)
{
_problemDetailsFactory = HttpContext?.RequestServices?.GetRequiredService<ProblemDetailsFactory>();
}
return _problemDetailsFactory;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_problemDetailsFactory = value;
}
}
Source
that could be mocked and populated when testing in isolation.
[TestMethod]
public async Task GetCallsProblemOnInvalidId() {
//Arrange
var problemDetails = new ProblemDetails() {
//...populate as needed
};
var mock = new Mock<ProblemDetailsFactory>();
mock
.Setup(_ => _.CreateProblemDetails(
It.IsAny<HttpContext>(),
It.IsAny<int?>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>())
)
.Returns(problemDetails)
.Verifyable();
var sut = new MyController(...);
sut.ProblemDetailsFactory = mock.Object;
//...
//Act
var result = await sut.Get(someInvalidId);
//Assert
mock.Verify();//verify setup(s) invoked as expected
//...other assertions
}
I came to this question via the related issue:
https://github.com/dotnet/aspnetcore/issues/15166
Nkosi correctly pointed to the background ProblemDetailsFactory.
Note that the issue has been fixed in .NET 5.x but NOT in LTS .NET 3.1.x as you can see in the source code referenced by Nkosi (by switching the branches/tags in Github)
As Nkosi said, the trick is to set the ProblemDetailsFactory property of your controller in your unit tests.
Nkosi suggested to mock the ProblemDetailsFactory, but doing as above, you can't verify the values of the Problem object in your unit tests.
An alternative is simply to set a real implementation of the ProblemDetailsFactory, for instance copy the DefaultProblemDetailsFactory from Microsoft (internal class) to your UnitTest projects:
https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/Infrastructure/DefaultProblemDetailsFactory.cs
Get rid of the options parameter there.
Then just set an instance of it in the controller in your unit test and see the returned object as expected!
To improve upon EricBDev's answer (to avoid having to create any implementation of ProblemsDetailFactory in your tests) and Nkosi's answer (to allow verifying the values used when creating the Problem), you can mock the ProblemsDetailFactory to return an empty ProblemsDetail (to avoid NRE) and then verify the calls to the mocked factory, to make sure the right status code, details, etc. are passed to it by the code under test.
Example: (using Moq)
// create the mock `ProblemDetailsFactory`
var problemDetailsFactoryMock = new Mock<ProblemDetailsFactory>();
// set it up to return an empty `Problems` object (to avoid the `NullReferenceException`s)
problemDetailsFactoryMock.Setup(p =>
p.CreateProblemDetails(
It.IsAny<HttpContext>(),
It.IsAny<int>(), // statusCode
It.IsAny<string>(), // title
It.IsAny<string>(), // type
It.IsAny<string>(), // detail
It.IsAny<string>()) // instance
).Returns(new ProblemDetails());
// your other test code here
// verify the arguments passed to `Problem(...)`
_problemDetailsFactoryMock.Verify(p =>
p.CreateProblemDetails(
It.IsAny<HttpContext>(),
(int)HttpStatusCode.Forbidden, // or whatever StatusCode you expect
default, // or whatever you expect for `Title`
default, // or whatever you expect for `Type`
It.Is<string>(s => s.Contains("whatever you expect in the Detail", StringComparison.OrdinalIgnoreCase)),
default // or whatever you expect for `Instance`
));
In your tests, if you first create a ControllerContext, then ProblemDetails should be created as expected while executing controller code.
...
MyController controller;
[Setup]
public void Setup()
{
controller = new MyController();
controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
// add other mocks or fakes
}
};
}
...
I'm new to testing .net core web APIs and am struggling to test the POST method on my API. I can assert the type of the ActionResult that is returned, but I can't seem to figure out how to compare the result to the fixture that I'm creating.
I've debugged the unit test and seen that the CreatedAtAction call is returning an ActionResult. The top-level Value is null. However, the Value for the Result has the item.
I feel like I'm missing a cast or something and my code should be expecting a CreatedAtActionResult or something, but I can't seem to figure out how to get this to work.
Here is the (POST) method from my controller:
[HttpPost]
public async Task<ActionResult<TodoItem>> AddTodoItem(TodoItem item)
{
_context.TodoItems.Add(item);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetTodoItem), new { id = item.Id }, item);
}
Here is my unit test:
[Fact]
public async Task PostTodoItem_ShouldPass()
{
var fixture = new Fixture();
var item = fixture.Create<TodoItem>();
var result = await _controller.AddTodoItem(item);
Assert.IsType<ActionResult<TodoItem>>(result); // WORKS
Assert.Equal(item, result.Value); // DOESN'T WORK!!
}
I'm using Autofixture and the EF in-memory database for testing. All of this is getting set up in a shared database fixture class which runs before the unit test.
Here is my debugger output.
This appears to be an oddity in the object model here. I dont know the reason though.
You call CreatedAtAction which returns a CreatedAtActionResult. That has a Value property which is an object. This is what you're passing the TodoItem into and you can see it in your debugger output.
This class does NOT inherit from ActionResult< T>, but thats what your function returns.
ActionResult< T> DOES have an implicit operator that allows it any type to be cast to it and one that is for ActionResult objects (not generic).
So when the compiler sees you returning a CreatedAtActionResult, it needs to turn that into the ActionResult< T> so it calls the implicit cast from ActionResult (as CreatedAtActionResult : ObjectResult : ActionResult).
Therefore to get the Todo for the comparison you want you need to:
Assert.Equal(item, (result.Result as CreatedAtActionResult).Value); // SHOULD WORK!!
Assert.Equal(item, result.Value); // DOESN'T WORK!!
Assert.Equal(item, ((ObjectResult)actual.Result).Value); // Try this it should work
I have some problems testing a singleton. When I run this code, I get an error in TestGetLogicalDevices(). CallTo() failed because service is no fake object. When I try to create a fake object (commented code), it gives an error because RestService is a singleton with private constructor. How can I create a fake object of this singleton?
private RestService service;
[TestInitialize]
public void Init()
{
//service = A.Fake<RestService>();
service = RestService.Instance;
service.CreateClient("test", "test");
}
[TestMethod]
public async Task TestGetLogicalDevices()
{
var logicalDevices = (List<LogicalDevice>)A.CollectionOfFake<LogicalDevice>(10);
A.CallTo(() => service.GetLogicalDevices()).Returns(Task.FromResult(logicalDevices));
List<LogicalDevice> collectedData = await service.GetLogicalDevices();
Assert.AreEqual(2, collectedData.Count);
}
public async Task<List<LogicalDevice>> GetLogicalDevices()
{
var response = await client.GetAsync(apiBaseUrl + "/logical-devices");
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
var logicalDevices = JsonConvert.DeserializeObject<List<LogicalDevice>>(json);
var sortedList = logicalDevices.OrderBy(logicalDevice => logicalDevice.Name).ToList();
return sortedList;
}
else
{
return null;
}
}
Update
I added the code of my method I want to test. Maybe someone has suggestions for better tests?
Note: I'm not sure I understand what you're trying to do. What are you trying to test exactly? In your test, you configure service.GetLogicalDevices() to return something, then you call service.GetLogicalDevices() and assert what it returns (which, unless FakeItEasy is broken, should be what you configured it to return). So, you're not actually testing the service... you're testing the mocking framework! Mocking frameworks like FakeItEasy are useful to mock the dependencies of the system under test (SUT), not the SUT itself. In your case, if the SUT is RestService, you need to mock the dependencies of RestService, not RestService itself. For instance, you could inject an HttpClient with a HttpMessageHandler that you control (see here for more details).
Now, to answer your actual question (assuming it's really RestService that you want to fake):
When I run this code, I get an error in TestGetLogicalDevices(). CallTo() failed because service is no fake object.
A.CallTo only works on fakes; FakeItEasy can't control the behavior of objects it didn't create.
When I try to create a fake object (commented code), it gives an error because RestService is a singleton with private constructor
RestService is a class, and FakeItEasy can create a fake for a class, but it does it by inheriting the class, so it needs an accessible constructor. Also, keep in mind that only virtual methods can be configured. GetLogicalDevices is not virtual, so the fake can't override its behavior.
You have two main options for faking RestService:
make the constructor protected rather than private, and make the methods virtual so that they can be overriden
create an IRestService interface that represents the "public contract" of the RestService class, and fake that interface instead of the class.
New to unit testing. I have a WPF client app hooked into a WCF service via basicHttpbinding. Everything works great. I'm using simple constructor Dependency Injection in my viewModel, passing in an IServiceChannel which I then call me service methods on e.g:
IMyserviceChannel = MyService;
public MyViewModel(IMyServiceChannel myService)
{
this.MyService = myService;
}
Private void GetPerson()
{
var selectedPerson = MyService.GetSelectedPerson();
}
I have then added an MS Test project in the client app and I'm trying to use Moq to mock my service:
[TestMethod]
public void GetArticleBody_Test_Valid()
{
// Create channel mock
Mock<IIsesServiceChannel> channelMock = new Mock<IIsesServiceChannel>(MockBehavior.Strict);
// setup the mock to expect the Reverse method to be called
channelMock.Setup(c => c.GetArticleBody(1010000008)).Returns("110,956 bo/d, 1.42 Bcfg/d and 4,900 bc/d. ");
// create string helper and invoke the Reverse method
ArticleDataGridViewModel articleDataGridViewModel = new ArticleDataGridViewModel(channelMock.Object);
string result = channelMock.GetArticleBody(1010000008);
//Assert.AreEqual("cba", result);
//verify that the method was called on the mock
channelMock.Verify(c => c.GetArticleBody(1010000008), Times.Once());
}
The test is failing with a System.NullReferenceException. Object reference not set to an instance of an object. at the method invocation here:
string result = articleDataGridViewModel.IsesService.GetArticleBody(1010000008);
so I'm wandering whether this is the best way to approach or am I better somehow mocking an isolated part of the viewModel which is applicable to the test?
The NullReferenceException is mybe thrown because you use MockBehavior.Strict. The documentation says:
Causes this mock to always throw an exception for invocations that don't have a corresponding setup.
Maybe the constructor of ArticleDataGridViewModel calls other methods of the service which you haven't set up.
Another issue is, that you are calling the mocked method directly. Instead you should call a method of your view model, which calls this method.
[TestMethod]
public void GetArticleBody_Test_Valid()
{
// Create channel mock
Mock<IIsesServiceChannel> channelMock = new Mock<IIsesServiceChannel>();
// setup the mock to expect the Reverse method to be called
channelMock.Setup(c => c.GetArticleBody(1010000008)).Returns("110,956 bo/d, 1.42 Bcfg/d and 4,900 bc/d. ");
// create string helper and invoke the Reverse method
ArticleDataGridViewModel articleDataGridViewModel = new ArticleDataGridViewModel(channelMock.Object);
string result = articleDataGridViewModel.MethodThatCallsService();
//Assert.AreEqual("cba", result);
//verify that the method was called on the mock
channelMock.Verify(c => c.GetArticleBody(1010000008), Times.Once());
}
Besides that I think there is no problem with your approach. Maybe the view model violates the single responsibility principle and does more than it should, but that's hard to tell on the basis of your code example.
EDIT: Here's a full example of how you could test something like this:
public interface IMyService
{
int GetData();
}
public class MyViewModel
{
private readonly IMyService myService;
public MyViewModel(IMyService myService)
{
if (myService == null)
{
throw new ArgumentNullException("myService");
}
this.myService = myService;
}
public string ShowSomething()
{
return "Just a test " + this.myService.GetData();
}
}
class TestClass
{
[TestMethod]
public void TestMethod()
{
var serviceMock = new Mock<IMyService>();
var objectUnderTest = new MyViewModel(serviceMock.Object);
serviceMock.Setup(x => x.GetData()).Returns(42);
var result = objectUnderTest.ShowSomething();
Assert.AreEqual("Just a test 42", result);
serviceMock.Verify(c => c.GetData(), Times.Once());
}
}
Without access to your viewmodel, there's only so much help that we can provide you.
However, this code:
Mock<IIsesServiceChannel> channelMock = new Mock<IIsesServiceChannel>(MockBehavior.Strict);
...
ArticleDataGridViewModel articleDataGridViewModel = new ArticleDataGridViewModel(channelMock.Object);
...
string result = articleDataGridViewModel.IsesService.GetArticleBody(1010000008);
Does not set up your IsesService. If it is not set up in your constructor, that means the IsesService is a null reference. You can't call a method on a null object.
Consider mocking out at a higher level of abstraction then the tight coupling you have with the tool your using.
Perhaps your view-model should rely on services and not a detail of the tool that your using (i.e. IIsesServiceChannel).
Here's an example:
Construct testable business layer logic