Does my unit test care about too much - c#

I have 2 concerns about my unit test method:
Do I test too much in one test method?
How can my test method name reflect all test expectations?
I asked myself when my method name says: ReturnInvalidModelState, then my 2 other Asserts are not correct. At least concerning the method name...
[Test]
public void Create_TemplateAlreadyExists_ReturnInvalidModelState()
{
// ARRANGE
TemplateViewModel templateViewModel = new TemplateViewModel {
Name = "MyTest"
};
Mock<ITemplateDataProvider> mock1 = new Mock<ITemplateDataProvider>();
Mock<IMappingEngine> mock2 = new Mock<IMappingEngine>();
TemplateController controller =
new TemplateController(mock1.Object, mock2.Object);
mock1.Setup(m => m.TemplateExists("MyTest")).Returns(true);
// Set ModelState.IsValid to false
controller.ModelState.AddModelError("Name",
"This name already exists.");
// ACT
ActionResult result = controller.Create(templateViewModel);
// ASSERT
Assert.IsFalse(controller.ModelState.IsValid);
Assert.IsInstanceOfType(typeof(PartialViewResult), result);
Assert.AreEqual(templateViewModel, ((PartialViewResult)result).Model);
}
[HttpPost]
public ActionResult Create(TemplateViewModel templateViewModel)
{
if (ModelState.IsValid
&& !_templateDataProvider.TemplateExists(templateViewModel.Name))
{
Template template =
Mapper.Map<TemplateViewModel, Template>(templateViewModel);
_templateDataProvider.AddTemplate(template);
return new JsonNetResult(new { success = true });
}
ModelState.AddModelError("Name", "This name already exists.");
return PartialView(templateViewModel);
}

Yes, I think that you are testing for too many things.
Start with renaming your test method. Your method signature should describe action, scenario and expected outcome.
If I were to rename your method, than I would end up with the following:
public void Create_DuplicateTemplate_ModelStateIsInvalidAndReturnsPartialViewResultAndPartialViewResultOfTypeTemplateViewModel()
{
}
Your test is concerned with three things, rather than one. When it fails, you won't know straight away why it has failed.
Consider re-factoring this into smaller tests and encapsulating some of the arrangement logic so that it can be re-used.
Edit:
You made a good point in your comment regarding single test method having a single assertion. I agree with you on that one, as good as it sounds, often it's not sufficient.
Say I have the following action method:
[HttpPost]
public ActionResult Register(NewUserViewModel newUser)
{
if (!ModelState.IsValid)
return View(newUser);
var newUserDTO = Mapper.Map<NewUserViewModel, NewUserDTO>(newUser);
var userDTO = UserManagementService.RegisterUser(newUserDTO);
var result = Mapper.Map<UserDTO, UserViewModel>(userDTO);
TempData.Add("RegisteredUser", result);
return RedirectToAction("RegisterSuccess");
}
I have the following unit test for this method:
[TestMethod]
public void Register_HttpPost_ValidViewModel_ReturnsRegisterSuccess()
{
// Arrange
var autoMapperMock = this.mockRepository.DynamicMock<IMapper>();
var userManagementServiceMock = this.mockRepository.DynamicMock<IUserManagementService>();
var invalidRegistrationViewModel = new NewUserViewModel
{
LastName = "Lastname",
FirstName = "Firstname",
Username = null
};
autoMapperMock.Expect(a => a.Map<UserDTO, UserViewModel>(Arg<UserDTO>.Is.Anything)).Repeat.Once().Return(null);
autoMapperMock.Expect(a => a.Map<NewUserViewModel, NewUserDTO>(Arg<NewUserViewModel>.Is.Anything)).Repeat.Once().Return(null);
userManagementServiceMock.Expect(s => s.RegisterUser(Arg<NewUserDTO>.Is.Anything)).Repeat.Once();
autoMapperMock.Replay();
var controller = new AccountController
{
Mapper = autoMapperMock,
UserManagementService = userManagementServiceMock
};
this.mockRepository.ReplayAll();
// Act
var result = (RedirectToRouteResult)controller.Register(invalidRegistrationViewModel);
// Assert
Assert.IsTrue((string)result.RouteValues["Action"] == "RegisterSuccess");
}
As you can see, I set up multiple expectations on my mock:
I expect AutoMapper to be called twice
I expect UserManagementService to be called once
At the end of the test I have a single assertion that checks whether user was re-directed to the correct route.
So where do I check my assertions? I create another method that makes sure that my expectations have been met:
[TestCleanup]
public void Cleanup()
{
try
{
this.mockRepository.VerifyAll();
}
finally
{
}
}
So you are right, I have three assertions instead of one, but I structure my code in such a way so it appears that I have only one assertion.

I would recomend moving all of the "Arrange" and "Act" code into a Setup() method, and split the rest into three tests. This will make each individual test much easier to read, and let you give each test a name that corresponds better to the actual assert it contains.
private TemplateViewModel _templateViewModel;
private ITemplateDataProvider _mock2;
private IMappingEngine _mock2;
private TemplateController _controller;
private ActionResult _result;
[Setup]
public void Setup(){
// ARRANGE
_templateViewModel = new TemplateViewModel { Name = "MyTest" };
_mock1 = new Mock<ITemplateDataProvider>();
_mock2 = new Mock<IMappingEngine>();
_controller = new TemplateController(_mock1.Object, _mock2.Object);
_mock1.Setup(m => m.TemplateExists("MyTest")).Returns(true);
// Set ModelState.IsValid to false
_controller.ModelState.AddModelError("Name",
"This name already exists.");
_result = controller.Create(_templateViewModel);
}
[Test]
public void Create_TemplateAlreadyExists_ModelStateIsInvalid()
{
Assert.IsFalse(_controller.ModelState.IsValid);
}
[Test]
public void Create_TemplateAlreadyExists_ResultIsPartialViewResult()
{
Assert.IsInstanceOfType(typeof(PartialViewResult), _result);
}
[Test]
public void Create_TemplateAlreadyExists_ResultModelMatchesTemplateModel()
{
Assert.AreEqual(_templateViewModel, ((PartialViewResult)_result).Model);
}

Related

Unit testing Delete Method of API producing -> Can not instantiate proxy of class

I'm trying to write a Unit test case for the delete method of the below controller
public class AssetProxyController : Controller
{
private IRiskAssetProxyService _assetProxyService;
public AssetProxyController( IRiskAssetProxyService assetProxyService)
{
_assetProxyService = assetProxyService;
}
[HttpDelete("{assetId}")]
public ActionResult Delete(string assetId)
{
if (_assetProxyService.DeleteAssetProxyMapping(assetId))
{
return Ok("AssetProxy Deleted");
}
else
{
return BadRequest("Unable to delete AssetProxy");
}
}
}
Test
[TestMethod]
public void Delete_ShouldReturnDeleteAssetProxy()
{
//Mock
var mockContext = new Mock<MainDBContext>();
var faker = AutoFaker.Create();
var assetProxyMappings = faker.Generate<List<AssetProxyMapping>>();
var mockAssetProxyDbSet = GetQueryableMockDbSet<AssetProxyMapping>(assetProxyMappings);
mockContext.Setup(c => c.AssetProxyMapping).Returns(mockAssetProxyDbSet);
//Test
var mocklogger = new Mock<ILogger<RiskDataControllerDbAccess>>();
var positiondbaccess = new Mock<RiskAssetProxyDbAccess>(mockContext,mocklogger);
var mockserviceLogger = new Mock<ILogger<RiskAssetProxyService>>();
var positionService = new Mock<RiskAssetProxyService>(positiondbaccess, mockserviceLogger);
var positionController = new AssetProxyController(positionService.Object);
Assert.AreEqual(true, true);
}
But i keep getting the exception
Test method Risk.DataService.UnitTests.API.AssetProxyControllerTests.Delete_ShouldReturnDeleteAssetProxy threw exception:
Castle.DynamicProxy.InvalidProxyConstructorArgumentsException: Can not instantiate proxy of class: Risk.DataServices.RiskAssetProxyService.
Could not find a constructor that would match given arguments:
Moq.Mock`1[Risk.DataServices.RiskAssetProxyDbAccess]
Moq.Mock`1[Microsoft.Extensions.Logging.ILogger`1[Risk.DataServices.RiskAssetProxyService]]
The currently shown test is completely over-engineered based on the shown subject under test.
Given the shown Delete controller action, the test case should be simplified
[TestMethod]
public void AssetProxy_Delete_Should_Return_Ok() {
//Arrange
string assetId = "assetId";
var serviceMock = new Mock<IRiskAssetProxyService>();
serviceMock.Setup(_ => _.DeleteAssetProxyMapping(assetId))
.Returns(true);
AssetProxyController controller = new AssetProxyController(serviceMock.Object);
//Act
ActionResult result = controller.Delete(assetId);
//Assert - using fluent assertions
result.Should().NotBeNull();
string expected = "AssetProxy Deleted";
OkObjectResult okResult = result as OkObjectResult;
okResult.Should().NotBeNull();
string actual = okResult.Value as string;
actual.Should().Be(expected); //same as Assert.AreEqual(expected, actual);
}
Another test case can be done to assert bad requests and would be similar to the case shown above except that the mocked service will be arranged to return false in order to cause the expected behavior and the assertion updated accordingly

Unit Test - Assert object returned by a controller action

How can I check if the value returned as part of that OkObjectResult has a count of 2 without changing any code in the controller action?
Here is my controller action
public IActionResult GetUserNames()
{
var users = _repository.GetUsers();
return Ok(users.Select(u => u.Name));
}
My unit test looks like this
[Fact]
public void GetUserNames_ValidRequest_ShouldReturnOk()
{
_repository
.Setup(r => r.GetUsers())
.Return(new List<User>
{
new User { Name = "SomeRandomName" },
new User { Name = "SomeRandomName2" }
});
var result = _controller.GetUserNames();
result.Should().BeOfType<OkObjectResult>();
// Code to check if 2 names are returned
}
I am using Mock and FluentAssertions in my unit test.
You should be able to do something like this:
var objectResult = Assert.IsType<OkObjectResult>(result);
var model = Assert.IsAssignableFrom<List<string>>(objectResult.Value);
Assert.Equal(2, model.Count);

How to unit test POST method

I have problems with testing post method.
In that method AntiForgeryToken is checked and I don't know to how to mock it.
Beside that everythings works.
Here my method I want to test:
[HttpPost]
//[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include="Id,Name,Manufacturer,CatalogNo")] Device device)
{
ValidateRequestHeader(Request);
if (String.IsNullOrWhiteSpace(device.Name)||String.IsNullOrWhiteSpace(device.Manufacturer)||String.IsNullOrWhiteSpace(device.CatalogNo))
{
ModelState.AddModelError("", "Niepoprawne dane");
return PartialView(device);
}
unitOfWork.deviceRepository.Insert(device);
unitOfWork.Save();
return Json(new { ok = true, newurl = Url.Action("Index") });
}
I have already mocked:
private IUnitOfWork fakeRepo;
DeviceController DC;
[TestInitialize]
public void Initialize()
{
Mock<IUnitOfWork> mock = new Mock<IUnitOfWork>();
mock.Setup(m => m.deviceRepository.Get(
It.IsAny<List<Expression<Func<Device, bool>>>>(),
It.IsAny<Func<IQueryable<Device>, IOrderedQueryable<Device>>>(),
null))
.Returns(new[] { new Device { Id = 1, Manufacturer = "a", Name = "b", CatalogNo = "x" } });
mock.Setup(m => m.deviceRepository.Get()).Returns(new[] { new Device { Id = 1, Manufacturer = "a", Name = "b", CatalogNo = "z" } });
fakeRepo = mock.Object;
DC = new DeviceController(fakeRepo);
}
But I'dont know how to method testing AntiforgeryToken
here code:
public void ValidateRequestHeader(HttpRequestBase request)
{
string cookieToken = "";
string formToken = "";
if (request.Headers["RequestVerificationToken"] != null)
{
string[] tokens = request.Headers["RequestVerificationToken"].Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
I can't comment out this call testing purpose but this solution seems little ugly.
Can You suggest any edits?
It looks like you need to mock HttpRequestBase and AntiForgery.
How do you mock them?
You wrap them in your own interfaces exposing the behaviour you need or may need in the very near future. In your production code you provide the real .NET implementations, in your tests, your mocks.
MSDN says AntiForgery.Validate
Validates that input data from an HTML form field comes from the user
who submitted the data.
the signature that takes two string arguments returns void.
Your mock IAntiForgeryValidator would have a Validate(string, string) method that also returns void.
public interface IAntiForgeryValidator
{
void Validate(string cookieToken, string formToken);
}
public class AntiForgeryValidator : IAntiForgeryValidator
{
public void Validate(string cookieToken, string formToken)
{
AntiForgery.Validate(cookieToken, formToken);
}
}
You can use a call back for void methods and verify that they were called the correct number of times:
antiForgeryMock.Setup(m => m.Validate(
It.IsAny<string>(),
It.IsAny<string>()))
.Callback((string cookieToken, string formToken) =>
{
// call back
});
antiForgeryMock.Verify(m => m.Validate(
It.IsAny<string>(),
It.IsAny<string>()), Times.Once());
You have another option (cheating) and that is to stub your call to ValidateRequestHeader() in Create(). This will enable you to test the rest of your code but is not reccomended because the real ValidateRequestHeader() could cause trouble in your production code if you leave the method un-tested.

How to Test Void Method using xUnit?

I am want to do a unit test of a method that return nothing. I am using xUnit for this. I search in Google but every where I saw methods are returning something.
Here is my code :
My Class :
public class ShopRepository : BaseRepository<ShopInformation>
{
public ShopRepository(IDbContext dbContext)
: base(dbContext)
{
}
public override void Update(ShopInformation entity)
{
if(GetAll().Any())
base.Update(entity);
else
base.Add(entity);
}
public ShopInformation ShopInformation()
{
return GetAll().FirstOrDefault();
}
}
My Test :
[Fact]
public void GetAllTest()
{
var data = new List<ShopInformation>
{
new ShopInformation { Name = "BBB" },
new ShopInformation { Name = "ZZZ" },
new ShopInformation { Name = "AAA" },
}.AsQueryable();
var mockSet = Mock.Create<DbSet<ShopInformation>>();
Mock.Arrange(() => ((IEnumerable<ShopInformation>)mockSet).GetEnumerator()).Returns(data.GetEnumerator);
Mock.Arrange(() => ((IQueryable<ShopInformation>)mockSet).Provider).Returns(data.Provider);
Mock.Arrange(() => ((IQueryable<ShopInformation>)mockSet).Expression).Returns(data.Expression);
Mock.Arrange(() => ((IQueryable<ShopInformation>)mockSet).ElementType).Returns(data.ElementType);
Mock.Arrange(() => ((IQueryable<ShopInformation>)mockSet).GetEnumerator()).Returns(data.GetEnumerator());
var interDbContext = Mock.Create<IDbContext>();
interDbContext.Arrange(x => x.Set<ShopInformation>()).Returns(mockSet);
var companyRepository = new ShopRepository(interDbContext);
companyRepository.Update(new ShopInformation());
//???????????????
}
I need to test Update Method of ShopRepository Class to ensure that base.Update(entity); is call. But don`t understand How to do it.
I am using :
Visual Studio 2013 Ultimate.
Just Mock 2013.3.1015
xUnit 1.9.2
There is 2 possible solutions:
If Update method modifies any value from ShopRepository class, you should perform some validation on them.
Mock out base.Update method to return "expected" and assert based on that.
Your test is named GetAllTest(), so it probably shouldn't be testing the Update() function.
Best way to do this is to return a bool for if it updated or not.
public bool Update(ShopInformation entity)
{
if(GetAll().Any())
{
base.Update(entity);
return true; // True because it updated
}
else
{
base.Add(entity);
return false; // False because it didn't
}
}
And then your test can Assert the expected answer.
// ... Setup test code
var hasUpdated = companyRepository.Update(new ShopInformation());
Assert.Equal(true, hasUpdated);
A point to note: If you are using a Repository, you should probably use Save as this will update changes made, but will also add new records if the record does not already exist.

How to unit test ASP.Net MVC controller that has private methods in the action?

I am having a hard time wrapping my head around a unit testing pattern when trying to test an ASP.Net MVC controller/action.
With the following code, I am trying to write a test for the ShowPerson() method:
public class PersonController : Controller
{
private IDataAccessBlock _dab;
public PersonController()
: this(new DataAccessBlock())
{ }
public PersonController(IDataAccessBlock dab)
{
_dab = dab;
}
public ActionResult ShowPerson(PersonRequestViewModel personRequest)
{
var person = GetPersonViewModel(personRequest);
return View("Person", person);
}
private PersonViewModel GetPersonViewModel(PersonRequestViewModel personRequest)
{
var personService = new CommonDomainService.PersonService(_dab);
var dt = personService.GetPersonInfo(personRequest.Id);
var person = new PersonViewModel();
if (dt.Rows.Count == 1)
{
person.FirstName = dt.Rows[0]r["FIRSTNAME"]);
person.LastName = dt.Rows[0]["LASTNAME"];
}
return person;
}
}
The test that I am using (using nUnit and Moq):
[Test]
public void ShowPerson_Action_Should_Return_Person_View()
{
// Arrange
string expected = "Person";
Mock<PersonRequestViewModel> personRequestViewModelMock = new Mock<PersonRequestViewModel>();
personRequestViewModelMock.SetupProperty(f => f.Id, 123456);
Mock<IDataAccessBlock> mockDab = new Mock<IDataAccessBlock>();
PersonController personController = new PersonController(mockDab.Object);
// Act
ViewResult result = personController.ShowPerson(personRequestViewModelMock.Object) as ViewResult;
// Assert
personRequestViewModelMock.Verify();
result.Should().Not.Be.Null();
if (result != null) Assert.AreEqual(expected, result.ViewName, "Unexpected view name");
}
Everything seems to go fine, until the line if (dt.Rows.Count == 1) is encountered. I get an "Object reference not set to an instance of an object."
I assume that there must be something funky with the way that the following two lines are written:
var personService = new CommonDomainService.PersonService(_dab);
var dt = personService.GetPersonInfo(personRequest.Id);
but I'm not sure where to go from here. I have a lot of code that would look like this. Am I doing something wrong, or is there an actual way to test this?
Thanks for any help or pointers.
Is your CommonDomainService.PersonService is some kind of webservice which is hosted in your webapplication, when you are running your tests your webapplication will not be running and service will not be accessible.
Ideally, your controller has a dependency on CommonDomainService.PersonService which you are creating in your private method,instead this should be injected into the Controller(like you do DataAccess block), and mock it up in your test method.
write private readonly IDataAccessBlock _dab; instead of private IDataAccessBlock _dab;

Categories