How to Test Void Method using xUnit? - c#

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.

Related

How can I mock a method within a method that I am mocking?

I want to test in my MVC app if I can create an object using xUnit. The create method in my repository looks like this:
public bool Create(Patient model)
{
try {
_db.patients.Add(model);
saveChanges();
return true
} catch {
return false;
}
}
public int saveChanges() {
return db.SaveChanges();
}
My test currently looks like this:
public void CanCreatePatient() {
using(var mock = AutoMock.GetLoose())
{
mock.Mock<IPatientRepo>().Setup(x => x.Create(GetSamplePatient()));
mock.Mock<IPatientRepo>().Setup(x => x.saveChanges()),Returns(1);
var cls = mock.Mock.Create<IPatientRepo>();
var actual = cls.Create(GetSamplePatient());
Assert.True(actual);
}
}
Im fairly new to xUnit and testing in dotnet. Currently this test always fails because the actual returns false. What can I do to solve this issue?

"No connection string named 'Entities' could be found in the application config file." error with Moq

I'm using Moq to provide a mocking context for my Oracle db. But when I call _context.Entry with the mocked context, I get an InvalidOperationException.
"No connection string named 'Entities' could be found in the application config file."
I'm already providing a mocked context, so not sure why it's still trying to read connection string to create the context.
// generated code for oracle db
public partial class Entities : DbContext
{
public Entities()
: base("name=Entities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<ACTIVITY_CODE> ACTIVITY_CODE { get; set; }
}
// my code
public partial class Entities : System.Data.Entity.DbContext
{
public Entities(string scon) : base(scon) { }
}
// my code
public partial class ActivityCodeService
{
private Entities _context;
public ActivityCodeService(Entities context)
{
this._context = context;
}
public ACTIVITY_CODE Update(ACTIVITY_CODE item)
{
ACTIVITY_CODE ret = null;
var found = Read(item.ACT_ID);
if (found != null)
{
_context.Entry<ACTIVITY_CODE>(found).CurrentValues.SetValues(item); // throws InvalidOperationException "No connection string named 'Entities' could be found in the application config file."
_context.SaveChanges();
ret = item;
}
return ret;
}
}
// test code
[TestMethod]
public void activity_code_update_test()
{
// arrange
var mockSet = new Mock<DbSet<ACTIVITY_CODE>>();
mockSet.As<IQueryable<ACTIVITY_CODE>>().Setup(o => o.Provider).Returns(testData.Provider);
mockSet.As<IQueryable<ACTIVITY_CODE>>().Setup(o => o.Expression).Returns(testData.Expression);
mockSet.As<IQueryable<ACTIVITY_CODE>>().Setup(o => o.ElementType).Returns(testData.ElementType);
mockSet.As<IQueryable<ACTIVITY_CODE>>().Setup(o => o.GetEnumerator()).Returns(testData.GetEnumerator());
var mockContext = new Mock<Entities>();
mockContext.Setup(c => c.ACTIVITY_CODE).Returns(mockSet.Object);
var expected = new ACTIVITY_CODE() { ACT_ID = 1, ACT_CODE = "code 2", ACT_DESC = "desc 2" };
var target = new ActivityCodeService(mockContext.Object);
// act
target.Update(expected);
}
But if I don't use _context.Entry, then the test runs fine which is expected. So does that mean _context.Entry is creating another internal context and ignoring my mocked context?
// my code
public ACTIVITY_CODE Update(ACTIVITY_CODE item)
{
var ret = _context.ACTIVITY_CODE.FirstOrDefault(o => o.ACT_ID == item.ACT_ID);
if (ret != null)
{
ret.ACT_CODE = item.ACT_CODE;
ret.ACT_DESC = item.ACT_DESC;
_context.SaveChanges(); // this will work fine with Moq's mocked context
}
return ret;
}
Entry isn't, and can't be, mocked by Moq as it's not virtual so it is still going to try to use the database that it believes is there. That's why it's looking for a connection string.
What I have been able to do which has worked well is to abstract that function call into a virtual method that I had enough control over to actually mock.
Alternatives:
There are some tools based on answers to other questions that have the same base problem. Looks like TypeMock and JustMock may be able to work around the issue.
Additionally, it looks like MS Fakes should be able to shim it. After a little investigation it looks like it'd work something like this:
ShimDbEntityEntry<TestModel> entryMock = new ShimDbEntityEntry<TestModel>();
ShimDbPropertyValues mockValues = new ShimDbPropertyValues();
mockValues.SetValuesObject = (newValues) => { }; // Manually do something here
entryMock.CurrentValuesGet = () => mockValues;
ShimDbContext.AllInstances.EntryOf1M0<TestModel>((ctx, target) => entryMock);

Mocking EF6 with validation that occurs on SaveChanges

I found a decent article to get me started on unit testing my Entity Framework-based application using Moq: https://msdn.microsoft.com/en-us/data/dn314429.aspx
This issue I'm having is that the SaveChanges method of the Mock does not appear to trigger the ValidateEntity method like it normally would. None of the validation settings I configured in the EntityTypeConfiguration are being thrown as a DbEntityValidationException.
For example, my AddRoles_Fails_For_Empty_Name tests to make sure that the service cannot add a role with an empty name. Either the IsRequired() configuration is not being applied, or the ValidateEntity method is not being called. I should mention that it works correctly if I use the actual context in the web app.
I've included some of my relevant unit testing, DbContext, and Service code below.
Am I doing something incorrectly? Are there any known issues or workarounds?
Role DB Map
public class RoleMap : EntityTypeConfiguration<Role>
{
public RoleMap()
{
ToTable("bm_Roles");
HasKey(r => r.Id);
Property(r => r.Name).IsRequired().HasMaxLength(100).HasIndex(new IndexAttribute("UX_Role_Name") { IsUnique = true });
Property(r => r.Description).HasMaxLength(500);
}
}
DbContext
public class BlueMoonContext : DbContext, IBlueMoonContext
{
public BlueMoonContext() : base("name=BlueMoon")
{
}
public DbSet<Role> Roles { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.AddFromAssembly(typeof(BlueMoonContext).Assembly);
}
public void MarkAsModified<T>(T entity) where T : class
{
entity.ThrowIfNull("entity");
Entry<T>(entity).State = EntityState.Modified;
}
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var result = base.ValidateEntity(entityEntry, items);
if (entityEntry.State == EntityState.Added || entityEntry.State == EntityState.Modified)
{
// Perform validations that require database lookups
if (entityEntry.Entity is Role)
{
ValidateRole((Role)entityEntry.Entity, result);
}
else if (entityEntry.Entity is User)
{
ValidateUser((User)entityEntry.Entity, result);
}
}
return result;
}
private void ValidateRole(Role role, DbEntityValidationResult result)
{
if (role.Name.HasValue() && !Roles.NameAvailable(role.Name, role.Id))
{
result.ValidationErrors.Add(new DbValidationError("Name", "Already in use"));
}
}
private void ValidateUser(User user, DbEntityValidationResult result)
{
if (user.UserName.HasValue() && !Users.UserNameAvailable(user.UserName, user.Id))
{
result.ValidationErrors.Add(new DbValidationError("UserName", "Already in use"));
}
if (user.Email.HasValue() && !Users.UserNameAvailable(user.UserName, user.Id))
{
result.ValidationErrors.Add(new DbValidationError("Email", "Already in use"));
}
}
}
Account Service
public class AccountService : BaseService, IAccountService
{
private IPasswordHasher _passwordHasher;
public AccountService(IBlueMoonContext context, IPasswordHasher passwordHasher) : base(context)
{
_passwordHasher = passwordHasher;
}
public ServiceResult CreateRole(Role role)
{
role.ThrowIfNull("role");
Context.Roles.Add(role);
return Save();
}
// Copied from base service class
protected ServiceResult Save()
{
var result = new ServiceResult();
try
{
Context.SaveChanges();
}
catch (DbEntityValidationException validationException)
{
foreach (var validationError in validationException.EntityValidationErrors)
{
foreach (var error in validationError.ValidationErrors)
{
result.AddError(error.ErrorMessage, error.PropertyName);
}
}
}
return result;
}
}
Unit Test
[TestFixture]
public class AccountServiceTests : BaseTest
{
protected Mock<MockBlueMoonContext> _context;
private IAccountService _accountService;
[TestFixtureSetUp]
public void Setup()
{
_context = new Mock<BlueMoonContext>();
var data = new List<Role>
{
new Role { Id = 1, Name = "Super Admin" },
new Role { Id = 2, Name = "Catalog Admin" },
new Role { Id = 3, Name = "Order Admin" }
}.AsQueryable();
var roleSet = CreateMockSet<Role>(data);
roleSet.Setup(m => m.Find(It.IsAny<object[]>())).Returns<object[]>(ids => data.FirstOrDefault(d => d.Id == (int)ids[0]));
_context.Setup(m => m.Roles).Returns(roleSet.Object);
// _context.Setup(m => m.SaveChanges()).Returns(0);
_accountService = new AccountService(_context.Object, new CryptoPasswordHasher());
}
[Test]
public void AddRole_Fails_For_Empty_Name()
{
var role = new Role { Id = 4, Name = "" };
var result = _accountService.CreateRole(role);
Assert.False(result.Success);
}
}
SaveChanges is a virtual method which means you invoke a fake method....
You can create your mock CallBase = true, but it is not a good idea(it miss the idea of UT):
_context = new Mock<BlueMoonContext>(){ CallBase = true };
The above code will use the real implementation of BlueMoonContext for any method/property which is not explicitly setup.
RoleMap is responsible for your DB stracture, you should test it as a part of integration test(with DB).
In my opinion you should create an integration tests to verify the integrity(for example; cover RoleMap) with your DB, And create a UT using the Throw setup to cover the catch section(it's a part of your unit):
_contest.Setup(x => x.SaveChanges())
.Throws(new DbEntityValidationException());
Edit to answer the OP question in the comment
no, you don't have to separate the built in validation, you have to create another test(integration test). In this test you'll verify the validation behaviour: insert an illegal entity, expect that exception will raise(using ExpectedExceptionAttribute) and then verify that the DB is empty... to apply this behaviour use this pattern:
try
{
\\...
\\try to commit
}
catch(DbEntityValidationException ex)
{
\\do some validation, then:
throw;\\for ExpectedExceptionAttribute
}
I looked over the api of EntityTypeConfiguration, I didn't saw any contact which allows to UT the rules(unless you use tools like MsFakes, TypeMock Isolator there is no way to verify the ToTable/HasKey/Property was called). The class is being in used inside EntityFramework(which is a part of the BCL) in the integration test you don't have to verify that EntityFramework work properly, you are going to verify that your custom rules was integrated and works as you expect(In this answer you can read the reason not to test a BCL classes).
So use Moq in the UTs of AccountService. Create an integration tests for BlueMoonContext and RoleMap(without Moq).
By the way #LadislavMrnka offer an interesting way to test(integration test) EntityTypeConfiguration

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.

Does my unit test care about too much

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);
}

Categories