We are creating unit tests for an application and ran into a problem creating certain tests.
We are unit testing the following Handle() method of the class ActivateCommandHandler:
public class ActivateCommand : IRequest<HttpResponseMessage>
{
public string Controller { get; set; }
public ActivateCommand(string controllername)
{
this.Controller = controllername;
}
}
public class ActivateCommandHandler : CommandHandler<ActivateCommand, HttpResponseMessage>
{
protected readonly ICommandsGateway _commandsGateway;
protected readonly EndpointSettings _endpoints;
protected readonly IUserProfile _userprofile;
public ActivateCommandHandler(IMediator mediator, ICommandsGateway commandsGateway, IOptions<EndpointSettings> endpoints, IValidationContext validationContext, IUserProfile currentUser) : base(mediator, validationContext, currentUser)
{
_commandsGateway = commandsGateway;
_endpoints = endpoints.Value;
_userprofile = currentUser;
}
public override async Task<HttpResponseMessage> Handle(ActivateCommand command, CancellationToken cancellationToken)
{
if (_endpoints.EndpointExists(command.Controller))
{
// Check whether the second server controller is deactivated
string peercontroller = _endpoints.GetPeerController(command.Controller);
if (!string.IsNullOrEmpty(peercontroller))
{
BaseRedundancySwitchStatus controllerStatus = await _commandsGateway.GetRedundancySwitchStatus(_endpoints.GetEndpointAddress(peercontroller));
if ((controllerStatus.CurrentState == "Activated") || (controllerStatus.CurrentState == "ActivatedWithWarning") || (controllerStatus.CurrentState == "Activating"))
{
var resp = new HttpResponseMessage(HttpStatusCode.Conflict)
{
Content = new StringContent($"{peercontroller},{controllerStatus.CurrentState}")
};
return resp;
}
}
var result = await _commandsGateway.PostActivateCommand(_endpoints.GetEndpointAddress(command.Controller));
return result;
}
else
{
throw new InvalidControllerNameException($"ERROR: The controller {command.Controller} does not exist as an endpoint on this Control Center!");
}
}
}
For this the following were mocked: _endpoints, command and _commandsGateway (interface). This works great for unit testing the parameter validation. But we now want to test the behaviour when the peercontroller status is set to a specific value.
To do this we are trying to mock out the function _commandsGateway.GetRedundancySwitchStatus(). The following is the actual test implementation. We mock the _commandsGateway.GetRedundancySwitchStatus() function to return the expected BaseRedundancySwitchStatus with CurrentState set to "Activated". After that we call the handler of the actual function to be tested and check whether we get the expected error.
[Fact]
public async void ShouldHaveErrors_PeerControllerStateActivated()
{
var command = new ActivateCommand("Server Controller Slave1");
BaseRedundancySwitchStatus result = new BaseRedundancySwitchStatus()
{
CurrentState = "Activated"
};
_commandsGateway
.Setup(s => s.GetRedundancySwitchStatus("Server Controller Slave1"))
.ReturnsAsync(result);
HttpResponseMessage res = await _handler.Handle(command, CancellationToken.None);
Assert.True(res.StatusCode == System.Net.HttpStatusCode.Conflict);
}
Debugging the code, when I step through the code in the Handle() method where _commandsGateway.GetRedundancySwitchStatus is called, I can see that _endpoints.GetEndpointAddress(command.Controller) (which is the parameter) is called and the correct value is returned. After this the debugger steps to the next line without any indication of having executed the mock GetRedundancySwitchStatus() function. Inspecting the controllerStatus variable the value is null. I would expect the value to be the BaseRedundancySwitchStatus object which is supposed to be returned by the mocked GetRedundancySwitchStatus() function.
Where are we going wrong?
Related
This is the class contains EnqueueJobAsync method which I want to write test for it :
public class ConsumerBaseForTesting
{
protected IJobStore JobStore { get; private set; }
public ConsumerBaseForTesting(IJobStore jobStore)
{
JobStore = jobStore;
}
public async Task<IJob> EnqueueJobAsync(IJob job)
=> await JobStore.CreateAsync(job);
}
This is my test which Fails and its actual return is always NULL !
public class ConsumerBaseTest
{
private readonly Mock<IJobStore> _moqIJobStore;
private readonly ConsumerBaseForTesting _consumerBase;
public ConsumerBaseTest()
{
_moqIJobStore = new Mock<IJobStore>();
_consumerBase = new ConsumerBaseForTesting(_moqIJobStore.Object);
}
[Theory]
[ClassData(typeof(JobClassForTesting))]
public async Task EnqueueJobAsyncTest(IJob job)
{
var jobResult = await _consumerBase.EnqueueJobAsync(job);
Assert.Equal(job, jobResult);
}
}
The mock needs to be setup to do two things in order to replicate the expected behavior.
It needs to return the passed job in a completed task.
//...
public ConsumerBaseTest() {
_moqIJobStore = new Mock<IJobStore>();
_consumerBase = new ConsumerBaseForTesting(_moqIJobStore.Object);
//setup the mock to capture and return the job when CreateAsync(IJob job) is invoked
_moqIJobStore
.Setup(_ => _.CreateAsync(It.IsAny<IJob>()))
.Returns((IJob x) => Task.FromResult(x)); }
//...
.Returns((IJob x) => Task.FromResult(x)) captures the argument and returns completed Task<IJob>
{
public class MyClass
{
// all the call to GetData() of apiHelper should pass through this method
public async Task<T> InitiateAPICallAsync<T>(Task<T> apiCall) where T : BaseResponse
{
var response = await apiCall;
// some common code work using response data
return response;
}
public async void MyFunc()
{
var helper = new APIHelper("1", "2");
//
var response1 = await InitiateAPICallAsync(helper.GetData<Response1>()); // correct way
var rewponse2 = await helper.GetData<Response1>(); // incorrect way, need to show warning
}
}
public class APIHelper
{
public APIHelper(string a, string b)
{
// some code
}
public async Task<T> GetData<T>()
{
await Task.Delay(1000); // network call
// other code
return default;
}
}
public class Response1 : BaseResponse { }
public class Response2 : BaseResponse { }
public class BaseResponse { }
}
in my application MyClass, there is a method named InitiateAPICallAsync(). All call to the GetData() method of APIHelper must be pass through this method. I need to showing warning, if GetAsync() method called directly without passing through InitiateAPICallAsync.
Note: It is a sample code snippet, where in my real time project the APIHelper represents a Connectivity library. and MyClass represents another library named service.
How to show warning for a method if it is called directly in c#
Using CallerMemberName attribute is core thread of the following solution, thanks for Fumeaux's comment, I tried place CallerMemberName attribute above GetData method directly to get the caller, but the result is MyFunc but not InitiateAPICallAsync. So I tried use delegate as the InitiateAPICallAsync parameter that could make sure GetData will called by InitiateAPICallAsync. The following code has been simplified.
public delegate Task<int> PrintCaller([CallerMemberName] string Caller = null);
public class MyClass
{
public async Task<string> InitiateAPICallAsync(PrintCaller apiCall)
{
var response = await apiCall();
return "Test";
}
public async void MyFunc()
{
var helper = new APIHelper();
var str1 = await InitiateAPICallAsync(new PrintCaller(helper.GetData));
var str2 = await helper.GetData();
}
}
public class APIHelper
{
public async Task<int> GetData([CallerMemberName] string Caller = null)
{
if (Caller == "InitiateAPICallAsync")
{
// do some thing
}
else
{
//Show Warning
var dialog = new MessageDialog("Waring!!! Please don't call it directly");
await dialog.ShowAsync();
}
return 0;
}
}
I would like to know if it is possible to mock the return of a method of a class that is instantiated within the target test method. The different point here is that to instantiate this object, it is necessary to pass in its constructor an object returned from a static class. Follow the example to be clearer.
public async Task Invoke(HttpContext httpContext)
{
GrpcChannel channel = GrpcChannel.ForAddress("address");
var client = new MethodDescriptorCaller(channel);
client.CallGrpcAsync(); //I need to mock the return of this method
}
The idea I had so far would be to encapsulate this process in another class. And using dependency injection to mock that return. But, I believe it is a very bad solution.
I don't think that creating a factory and inject it, it's a bad solution at all IMO
I think something like this should it...
interface IMethodDescriptorCallerFactory
{
MethodDescriptorCaller Build(GrpcChannel channel);
}
class MethodDescriptorCallerFactory : IMethodDescriptorCallerFactory
{
MethodDescriptorCaller Build(GrpcChannel channel)
{
return new MethodDescriptorCaller(channel);
}
}
class Foo
{
IMethodDescriptorCallerFactory _methodDescriptorCallerFactory;
public Foo(IMethodDescriptorCallerFactory methodDescriptorCallerFactory)
{
_methodDescriptorCallerFactory = methodDescriptorCallerFactory
}
public async Task Invoke(HttpContext httpContext)
{
GrpcChannel channel = GrpcChannel.ForAddress("address");
var client = _methodDescriptorCallerFactory.Build(channel);
client.CallGrpcAsync(); //I need to mock the return of this method
}
}
class FooTest
{
[Fact]
public async Task Test_Invoke()
{
var methodDescriptorCaller = Mock.Of<MethodDescriptorCaller>(m =>
m.CallGrpcAsync() == Task.Completed
);
var methodDescriptorCallerFactory = Mock.Of<IMethodDescriptorCallerFactory>(m =>
m.Build(It.IsAny<GrpcChannel>()) == methodDescriptorCaller
);
var foo = new Foo(methodDescriptorCallerFactory);
var httpContext = Mock.Of<HttpContext>();
await foo.Invoke(httpContext);
Mock.Get(methodDescriptorCallerFactory).VerifyAll();
Mock.Get(methodDescriptorCaller).VerifyAll();
}
}
I've an api with few methods in it. I need to authenticate user with Windows Authentication before it hits any of the controller methods. To achieve this, I've written a private method to get the UserIdentity and assign it to a private bool variable.
Each of the controller methods will first check the bool to verify the user and if it fails, will throw 401 back to the front end.
Whilst writing unit test case to the controller methods, I'm not sure how to mock the UserIdentity or should I update the controller in a way to allow the testing of UserIdentity
Controller :
public class DefaultController : ApiController
{
private bool isUsrAuthenticated;
public DefaultController()
{
AuthenticateUser();
}
private void AuthenticateUser()
{
isUsrAuthenticated = User.Identity.IsAuthenticated;
}
[HttpGet]
public IHttpActionResult GetProducts()
{
if (isUsrAuthenticated) { // do something }
else { // throw 401 }
}
[HttpPost]
public IHttpActionResult Update()
{
if (isUsrAuthenticated) { // do something }
else { // throw 401 }
}
}
The Unit Test results always return 401.
At run-time the ApiController.User property is set well after the constructor of the controller is invoked. That would mean that your current flow of calling it in the constructor like
public DefaultController() {
AuthenticateUser();
}
is flawed and will not provide the expected behavior.
Lets fix the code first, then look at the unit test.
Use a property instead like
private bool IsUserAuthenticated {
get {
return User.Identity.IsAuthenticated;
}
}
and refactor the code accordingly
public class DefaultController : ApiController {
private bool IsUserAuthenticated {
get {
return User.Identity.IsAuthenticated;
}
}
[HttpGet]
public IHttpActionResult GetProducts() {
if (IsUserAuthenticated) { // do something }
else { // throw 401 }
}
[HttpPost]
public IHttpActionResult Update() {
if (IsUserAuthenticated) { // do something }
else { // throw 401 }
}
//...
}
Now, for testing the ApiController, the User property can be set while arranging the unit test in order for the test to be exercised as expected
For example
[TestMethod()]
public void DefaultController_Should_GetProducts() {
//Arrange
var username = "name_here";
var userId = 2;
var identity = new GenericIdentity(username, "");
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Name, username));
var principal = new GenericPrincipal(identity, roles: new string[] { });
var user = new ClaimsPrincipal(principal);
var controller = new DefaultController() {
User = user //<-- Set the User on the controller directly
};
//Act
var actionResult = controller.GetProducts();
//Assert
//...
}
I'm trying to Setup a Mock call to a the IModelFactory interface that I have.
here is the IModelFactory interface
public interface IModelFactory
{
MaterialAcceptedModel Create(MaterialAccepted model);
MaterialAccepted Parse(MaterialAcceptedModel model);
}
This is the ModelFactory class which implements the IModelFactor interface (I've only add here the method I'm trying to test, no need to add the implementation of the Create Method)
public class ModelFactory : IModelFactory
{
private UrlHelper _urlHelper;
private IRTWRepository _repo;
//private IKeysGeneratorService _keysGen;
private IGeocodeService _geocoder;
public ModelFactory(HttpRequestMessage request, IRTWRepository repo,IGeocodeService geocoder)
{
_urlHelper = new UrlHelper(request);
_repo = repo;
_geocoder = geocoder;
}
#region Parses
public MaterialAccepted Parse(MaterialAcceptedModel model)
{
try
{
if (!string.IsNullOrWhiteSpace(model.category))
{
var category = _repo.CategoryRepository.Get(model.category);
if (category == null) return null;
var entry = new MaterialAccepted()
{
business = model.business,
businessService = model.businessService,
residential = model.residential,
residentialService = model.residentialService,
note = model.note,
Category = category
};
return entry;
}
return null;
}
catch
{
return null;
}
}
#endregion
}
I'm using a BaseAPiController that contains the repo and configuration Interfaces
public class BaseApiController : ApiController
{
IRTWRepository _repository;
IModelFactory _modelFactory;
IConfiguration _configuration;
IGeocodeService _geocoder;
public BaseApiController(IRTWRepository repository,IConfiguration configuration)
{
_repository = repository;
_configuration = configuration;
}
protected IRTWRepository TheRepository
{
get
{
return _repository;
}
}
protected IConfiguration TheConfiguration
{
get
{
return _configuration;
}
}
protected IModelFactory TheModelFactory
{
get
{
_geocoder = new GeocodeService(_configuration.GetValue("geocodeGoogleApiKey"));
if (_modelFactory == null)
{
_modelFactory = new ModelFactory(this.Request, _repository,_geocoder);
}
return _modelFactory;
}
}
Here is the action method in the controller I'm trying to test
[HttpPost]
[Route("api/recyclecenters/{rcid}/materials/")]
public IHttpActionResult Post(int rcid, [FromBody]MaterialAcceptedModel model)
{
try
{
if (model != null)
{
var recycleCenter = TheRepository.RecycleCenterRepository.Get(rcid);
if (recycleCenter == null)
return NotFound();
if (!ModelState.IsValid)
return BadRequest(ModelState);
var entity = TheModelFactory.Parse(model);
if (entity == null) return BadRequest("Could not read material accepted in body");
if (TheRepository.MaterialAcceptedRepository.Get(recycleCenter.RecycleCenterId, entity.Category.name) != null)
return Conflict();
recycleCenter.Materials.Add(entity);
if (TheRepository.SaveAll())
{
string locationHeader = Url.Link("Materials", new { rcid = rcid, name = model.category.ToLower() });
return Created<MaterialAcceptedModel>(locationHeader, TheModelFactory.Create(entity));
}
return BadRequest("Could not save to the database");
}
return BadRequest();
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
This is the line that returns null even though I mocked it up on my test method
var entity = TheModelFactory.Parse(model);
and this one is my TestClass
namespace API.Tests.Web
{
[TestClass]
public class MaterialsControllerTest
{
private Mock<IRTWRepository> repository;
private Mock<IModelFactory> factory;
private Mock<IConfiguration> configuration;
private Mock<IRTWAPIIdentityService> identityService;
private MaterialsController controller;
RecycleCenter recycleCenter;
private MaterialAccepted CreateMaterial()
{
return new MaterialAccepted()
{
business = true,
businessService = EnumRecycleCenterService.Dropoff,
residential = false,
residentialService = EnumRecycleCenterService.Pickup,
note = "this a note",
Category = new Category()
{
name = "Books"
}
};
}
[TestInitialize]
public void Initialize()
{
repository = new Mock<IRTWRepository>();
factory = new Mock<IModelFactory>();
configuration = new Mock<IConfiguration>();
identityService = new Mock<IRTWAPIIdentityService>();
controller = new MaterialsController(repository.Object,configuration.Object);
controller.Request = new HttpRequestMessage();
recycleCenter = new RecycleCenter(){RecycleCenterId = 1};
}
[TestMethod]
public void Post_ShouldReturnConflictIfTheRecycleCenterAlreadyTakesMaterial()
{
//arrange
repository.Setup(r => r.RecycleCenterRepository.Get(It.IsAny<int>())).Returns(() => recycleCenter);
factory.Setup(f => f.Parse(new MaterialAcceptedModel())).Returns(() => new MaterialAccepted());
configuration.Setup(c => c.GetValue(It.IsAny<string>())).Returns(() => "APIKEY");
repository.Setup(r => r.MaterialAcceptedRepository.Get(It.IsAny<int>(), It.IsAny<string>())).Returns(() => null);
//act
var actionResult = controller.Post(It.IsAny<int>(),new MaterialAcceptedModel());
//assert
Assert.IsInstanceOfType(actionResult, typeof(ConflictResult));
}
}
}
This is the line that's not working because it always return null instead of a new instance of MaterialAccepted
factory.Setup(f => f.Parse(new MaterialAcceptedModel())).Returns(() => new MaterialAccepted());
I tried f.Parse(It.IsAny()) but still doesn't work.
To clarify
the above line of code is returning null because is not mocking the f.Parse() call, instead is executing it and returning null because the if condition I have on that method
Anyone could explain why the Setup is not working?
Setting up your Mock using It.IsAny will work:
factory.Setup(f => f.Parse(It.IsAny<MaterialAcceptedModel>()))
.Returns(() => new MaterialAccepted());
However, as has been said by #Macilquham, I can't see where your are passing the Mock to your controller in the supplied code so that it is used by the production code.
If you don't call the method on your Mock object, which you don't, you're currently calling the method on the instance of the real object created by your base class, then it doesn't matter how you set up your mock it's not going to work. If you are able to change your base class, then doing something like this would allow you to work around your problem:
// Add defaulted parameter to base class to allow modelFactory creation
// to be overridden/injected (this will prevent the current default behaviour
// when fetching the factory, if a non-null is passed in)
public BaseApiController(IRTWRepository repository,IConfiguration configuration,
IModelFactory modelFactory = null)
{
_modelFactory = modelFactory;
}
Modify your sut constructor to allow you to supply a modelFactory (again, default it to null), then amend your test as appropriate:
controller = new MaterialsController(repository.Object,configuration.Object,
factory.Object);
You don't seem to be injecting in the IModelFactory into the controller. You need to make sure that your production code is using the Mock you are setting up in the test.
Mock cannot return null directly.
The trick is just to create a null object.
Assuming the object returned is of type class Material:
Material nullMaterial = null;
...
repository.Setup(r => r.MaterialAcceptedRepository
.Get(It.IsAny<int>(), It.IsAny<string>()))
.Returns(nullMaterial);
This should solve your problem