I'm getting into writing unit testing and have implemented a nice repository pattern/moq to allow me to test my functions without using "real" data. So far so good.. However..
In my repository interface for "Posts" IPostRepository I have a function:
Post getPostByID(int id);
I want to be able to test this from my Test class but cannot work out how.
So far I am using this pattern for my tests:
[SetUp]
public void Setup()
{
mock = new Mock<IPostRepository>();
}
[Test]
public void someTest()
{
populate(10); //This populates the mock with 10 fake entries
//do test here
}
In my function "someTest" I want to be able to call/test the function GetPostById. I can find the function with mock.object.getpostbyid but the "object" is null.
Any help would be appreciated :)
iPostRepository:
public interface IPostRepository
{
IQueryable<Post> Posts {get;}
void SavePost(Post post);
Post getPostByID(int id);
}
I'm not sure what unit testing framework you are using, but I am using NUnit. I'm not a unit testing pro, but I know enough to get me started and to get results.
I normally have a service layer, and this will call my post repository:
public class PostService
{
private readonly IPostRepository postRepository;
public PostService(IPostRepository postRepository)
{
if (postRepository== null)
{
throw new ArgumentNullException("postRepository cannot be null.", "postRepository");
}
this.postRepository = postRepository;
}
public Post GetPostById(int id)
{
return postRepository.GetPostById(id);
}
}
Your unit tests could look like this:
[TestFixture]
public class PostServiceTests
{
private PostService sut;
private Mock<IPostRepository> postRepositoryMock;
private Post post;
[SetUp]
public void Setup()
{
postRepositoryMock = new Mock<IPostRepository>();
sut = new PostService(postRepositoryMock.Object);
post = new Post
{
Id = 5
};
}
[Test]
public void GetPostById_should_call_repository_to_get_a_post_by_id()
{
int id = 5;
postRepositoryMock
.Setup(x => x.GetPostById(id))
.Returns(post).Verifiable();
// Act
Post result = sut.GetPostById(id);
// Assert
Assert.AreEqual(post, result);
postRepositoryMock.Verify();
}
}
I hope this helps.
If you want your mock object to return a result (not null), you need to set it up:
mock.Setup( m => m.GetPostByID( 5 ) ).Returns( new Post() );
What you return exactly is up to you of course.
Update:
If you need to use the method parameters you can also setup a CallBack. For example:
mock.Setup( m => m.GetPostByID( It.IsAny<int>() ) )
.Callback( id => return new Post{ Id = id; } );
This may make your setup code much easier since you don't need to prime the mock with data.
If you want to test the real implementation of GetPostById, do so via the real implementation of IPostRepository. Moq (and mocks in general) are only for situation where you don't want to use the real thing.
In other words prime your database with some posts, new up the real repository, call GetPostById and make assertions on the result. This is not strictly a unit test though, but an integration test because it includes the database.
Related
I have two versions of an API.
The second version of API will be having only one action method instead of two action methods in first version of API.
Second version of API action method will basically combine responses of first version of API's both action methods and return combined response to client.
Example code as follows:
[ApiController]
[Route("[controller]")]
public class NumbersV1Controller : ControllerBase
{
private readonly ILogger<NumbersV1Controller> _logger;
public NumbersV1Controller(ILogger<NumbersV1Controller> logger)
{
_logger = logger;
}
[HttpGet]
public int Get()
{
return 1;
}
[HttpPost]
public int Post()
{
return 2;
}
}
[ApiController]
[Route("[controller]")]
public class NumbersV2Controller : ControllerBase
{
private readonly ILogger<NumbersV2Controller> _logger;
public NumbersV2Controller(ILogger<NumbersV2Controller> logger)
{
_logger = logger;
}
[HttpPost]
public IEnumerable<int> Get()
{
// Method 1: Make a direct HTTP request.
// int response1 = HTTPClientHelper.GetRequest("Get", "NumbersV1");
// int response2 = HTTPClientHelper.PostRequest("Post", "NumbersV1");
// Method 2: Use instances and set controller context.
NumbersV1Controller numbersV1Controller = new NumbersV1Controller(null);
numbersV1Controller.ControllerContext = this.ControllerContext;
int response1 = numbersV1Controller.Get();
int response2 = numbersV1Controller.Post();
// Method 3: Use RedirectToAction method.
// RedirectToActionResult response1 = RedirectToAction("Get", "NumbersV1");
// RedirectToActionResult response2 = RedirectToAction("Post", "NumbersV1");
return new List<int>() { response1, response2 };
}
}
Method 1: Make a direct HTTP request.
It works perfectly but it is having additional boilerplate code and also it like making a new network call.
Method 2: Use instances and set controller context.
Not sure if this will work perfectly like can I access the Request object in version 1 controller and not sure how to initialize the version 2 controller will multiple injected objects
Method 3: Use RedirectToAction method.
I was assuming RedirectToAction will work but I don't see the result of the Action method in response object RedirectToActionResult.
What are the best options available for doing this in .NET Web API or is there any other way of doing this elegently?
Avoid using method 2 / method 3. Why? It violates so many patterns and performance will be an issue.
Method 1 is average if you really want to do it that way but will cost a network call though.
Method 4:
You can call directly inline business logic code from your V2 controller. If you already separated your business logic code to an individual service then you need to call it from your controller.
I have introduced a new class to do all the logical operations. You might have a similar one / many service classes for handling business requirements.
Let me give you an example:
public class Number1Controller : BaseController {
// You can use DI container to resolve this. I am using this as an example.
private readonly Service _service = new();
[HttpGet("{id}")]
public int GetById(int id) => _service.GetById(id);
[HttpGet("{name}")]
public string GetByName(string name) => _service.GetByName(name);
}
public class Number2Controller : BaseController {
// You can use DI container to resolve this. I am using this as an example.
private readonly Service _service = new();
[HttpGet("{id}")]
public int GetById(int id) => _service.GetById(id);
[HttpGet("{name}")]
public string GetByName(string name) => _service.GetByName(name);
}
// Business Logic Service
public class Service {
public int GetById(int id) => 1;
public string GetByName(string name) => "Stack Over Flow";
}
My Audit logs are getting out of hand so I decided I want to only audit all requests which basically are not a Get request. Is there a very simply way to do this from configuration?
The documentation here:
https://aspnetboilerplate.com/Pages/Documents/Audit-Logging
Says:
Note: In addition to the standard audit configuration, MVC and ASP.NET
Core modules define configurations to enable/disable audit logging for
actions.
But I could not find more information about what exactly this means.
As a last resort, I know it would work if I went to every class and added [DisableAuditing] and then [Audited] on the non-Get endpoints, but that seems a bit messy.
Best soltuion: I just want to have a simply way to select only non-GET requests and audit them.
Second best solution:
I just want to have only [Audited] methods audited. I don't want to have to go and write [DisabledAuditing] on every class.
You can create an AuditStore to do that, and then replace the original AuditStore in service YourAplicationNameCoreModule
Here is the example
public class YourAuditStore : AuditingStore
{
public ILogger<AuditingStore> Logger { get; set; }
private readonly IRepository<AuditLog, long> _auditLogRepository;
private readonly ISettingManager _settingManager;
public YourAuditStore(IRepository<AuditLog, long> auditLogRepository, ISettingManager settingManager) : base(auditLogRepository)
{
_auditLogRepository = auditLogRepository;
_settingManager = settingManager;
}
public override async Task SaveAsync(AuditInfo auditInfo)
{
AuditLog auditLog = new AuditLog();
bool logErrorsOnly = await _settingManager.GetSettingValueAsync<bool>(AppSettings.Logging.LogOnErrorsOnly);
var exceptionMessage = auditInfo.Exception != null ? auditInfo.Exception.ToString() : null;
if ((logErrorsOnly && exceptionMessage != null) || !logErrorsOnly)
{
auditLog = await _auditLogRepository.InsertAsync(AuditLog.CreateFromAuditInfo(auditInfo));
}
}
}
As you can see, you can filter whatever you want in SaveAsync method as it recieve the AuditInfo, you can check if method is different to Get then save
Add the next code to YourApplicationNameCoreModule on PreInitialize method
public override void PreInitialize()
{
Configuration.ReplaceService<IAuditingStore, YourAuditStore>();
}
I'm trying to do a test on my controllers which get data from repository classes.
This is the part of the repository I want to test:
public class NewsRepository
{
public IEnumerable<NewsItem> GetNews()
{
var result = (from n in n_db.NewsItems
orderby n.ID descending
select n).Take(3);
return result;
}
}
Just some small code to get how the testing works.
In my HomeController I've got this code inside the Index():
public ActionResult Index()
{
ViewBag.Message = "Announcements";
NewsRepository n_rep = new NewsRepository();
var model = i_rep.GetNews();
return View(model);
}
I am completely new to testing so all explanations would be great.
Thanks.
Your controller is impossible to be unit tested in isolation because it is strongly coupled with your repository on the following line:
NewsRepository n_rep = new NewsRepository();
You have simply hardcoded a specific implementation of the repository and in your unit test you cannot mock it. In order to do this properly you should start by defining an abstraction over this repository:
public interface INewsRepository
{
IEnumerable<NewsItem> GetNews();
}
and then have your specific repository implement this interface:
public class NewsRepository : INewsRepository
{
...
}
Ok now that we have an abstraction let's weaken the coupling between your data access and controller logic by using this abstraction:
public class NewsController: Controller
{
private readonly INewsRepository repository;
public NewsController(INewsRepository repository)
{
this.repository = repository;
}
public ActionResult Index()
{
ViewBag.Message = "Announcements";
var model = this.repository.GetNews();
return View(model);
}
}
Alright, now you have a controller that is no longer tightly coupled with some specific implementation. You could pickup your favorite mock framework and write a unit test. For example with NSubstitute here's how the unit test for the Index action might look like:
[TestMethod]
public void Index_Action_Fetches_Model_From_Repo()
{
// arrange
var repo = Substitute.For<INewsRepository>();
IEnumerable<NewsItem> expectedNews = new[] { new NewsItem() };
repo.GetNews().Returns(expectedNews);
var sut = new NewsController(repo);
// act
var actual = sut.Index();
// assert
Assert.IsInstanceOfType(actual, typeof(ViewResult));
var viewResult = actual as ViewResult;
Assert.AreEqual(expectedNews, viewResult.Model);
}
And that's pretty much it. Your controller is now easily unit testable in isolation. You don't need to be setting up databases or whatever. That's not the point to test the controller logic.
We are doing TDD for quite a while and we are facing some concerns when we refactor. As we are trying to respect as much as we can the SRP (Single responsibility principle), we created a lot of composition that our classes use to deal with common responsibilities (such as validation, logging, etc..).
Let's take a very simple example :
public class Executioner
{
public ILogger Logger { get; set; }
public void DoSomething()
{
Logger.DoLog("Starting doing something");
Thread.Sleep(1000);
Logger.DoLog("Something was done!");
}
}
public interface ILogger
{
void DoLog(string message);
}
As we use a mocking framework, the kind of test that we would do for this situation would be somthing like
[TestClass]
public class ExecutionerTests
{
[TestMethod]
public void Test_DoSomething()
{
var objectUnderTests = new Executioner();
#region Mock setup
var loggerMock = new Mock<ILogger>(MockBehavior.Strict);
loggerMock.Setup(l => l.DoLog("Starting doing something"));
loggerMock.Setup(l => l.DoLog("Something was done!"));
objectUnderTests.Logger = loggerMock.Object;
#endregion
objectUnderTests.DoSomething();
loggerMock.VerifyAll();
}
}
As you can see, the test is clearly aware of the method implementation that we are testing. I have to admit that this example is too simple, but we sometimes have compositions that cover responsibilities that don't add any value to a test.
Let's add some complexity to this example
public interface ILogger
{
void DoLog(LoggingMessage message);
}
public interface IMapper
{
TTarget DoMap<TSource, TTarget>(TSource source);
}
public class LoggingMessage
{
public string Message { get; set; }
}
public class Executioner
{
public ILogger Logger { get; set; }
public IMapper Mapper { get; set; }
public void DoSomething()
{
DoLog("Starting doing something");
Thread.Sleep(1000);
DoLog("Something was done!");
}
private void DoLog(string message)
{
var startMessage = Mapper.DoMap<string, LoggingMessage>(message);
Logger.DoLog(startMessage);
}
}
Ok, this is an example. I would include the Mapper stuff within the implementation of my Logger and keep a DoLog(string message) method in my interface, but it's an example to demonstrate my concerns
The corresponding test leads us to
[TestClass]
public class ExecutionerTests
{
[TestMethod]
public void Test_DoSomething()
{
var objectUnderTests = new Executioner();
#region Mock setup
var loggerMock = new Mock<ILogger>(MockBehavior.Strict);
var mapperMock = new Mock<IMapper>(MockBehavior.Strict);
var mockedMessage = new LoggingMessage();
mapperMock.Setup(m => m.DoMap<string, LoggingMessage>("Starting doing something")).Returns(mockedMessage);
mapperMock.Setup(m => m.DoMap<string, LoggingMessage>("Something was done!")).Returns(mockedMessage);
loggerMock.Setup(l => l.DoLog(mockedMessage));
objectUnderTests.Logger = loggerMock.Object;
objectUnderTests.Mapper = mapperMock.Object;
#endregion
objectUnderTests.DoSomething();
mapperMock.VerifyAll();
loggerMock.Verify(l => l.DoLog(mockedMessage), Times.Exactly(2));
loggerMock.VerifyAll();
}
}
Wow... imagine that we would use another way to translate our entities, I would have to change every tests that has some method that uses the mapper service.
Anyways, we really feel some pain when we do major refactoring as we need to change a bunch of tests.
I'd love to discuss about this kind of problem. Am I missing something? Are we testing too much stuff?
Tips:
Specify exactly what should happen and no more.
In your fabricated example,
Test E.DoSomething asks Mapper to map string1 and string2 (Stub out Logger - irrelevant)
Test E.DoSomething tells Logger to log mapped strings (Stub/Fake out Mapper to return message1 and message2)
Tell don't ask
Like you've yourself hinted, if this was a real example. I'd expect Logger to handle the translation internally via a hashtable or using a Mapper. So then I'd have a simple test for E.DoSomething
Test E.DoSomething tells Logger to log string1 and string2
The tests for Logger would ensure L.Log asks mapper to translate s1 and log the result
Ask methods complicate tests (ask Mapper to translate s1 and s2. Then pass the return values m1 and m2 to Logger) by coupling the collaborators.
Ignore irrelevant objects
The tradeoff for isolation via testing interactions is that the tests are aware of implementation.
The trick is to minimize this (via not creating interfaces/specifying expectations willy-nilly). DRY applies to expectations as well. Minimize the amount of places that an expectation is specified... ideally Once.
Minimize coupling
If there are lots of collaborators, coupling is high which is a bad thing. So you may need to rework your design to see which collaborators don't belong at the same level of abstraction
Your difficulties come from testing behavior rather than state. If you would rewrite the tests so that you look at what's in the log rather than verifying that the call to the log is made, your tests wouldn't break due to changes in the implementation.
I have a repository pattern i created on top of the ado.net entity framework. When i tried to implement StructureMap to decouple my objects, i kept getting StackOverflowException (infinite loop?). Here is what the pattern looks like:
IEntityRepository where TEntity : class
Defines basic CRUD members
MyEntityRepository : IEntityRepository
Implements CRUD members
IEntityService where TEntity : class
Defines CRUD members which return common types for each member.
MyEntityService : IEntityService
Uses the repository to retrieve data and return a common type as a result (IList, bool and etc)
The problem appears to be with my Service layer. More specifically with the constructors.
public PostService(IValidationDictionary validationDictionary)
: this(validationDictionary, new PostRepository())
{ }
public PostService(IValidationDictionary validationDictionary, IEntityRepository<Post> repository)
{
_validationDictionary = validationDictionary;
_repository = repository;
}
From the controller, i pass an object that implements IValidationDictionary. And i am explicitly calling the second constructor to initialize the repository.
This is what the controller constructors look like (the first one creates an instance of the validation object):
public PostController()
{
_service = new PostService(new ModelStateWrapper(this.ModelState));
}
public PostController(IEntityService<Post> service)
{
_service = service;
}
Everything works if i don't pass my IValidationDictionary object reference, in which case the first controller constructor would be removed and the service object would only have one constructor which accepts the repository interface as the parameter.
I appreciate any help with this :) Thanks.
It looks like the circular reference had to do with the fact that the service layer was dependent on the Controller's ModelState and the Controller dependent on the Service layer.
I had to rewrite my validation layer to get this to work. Here is what i did.
Define generic validator interface like below:
public interface IValidator<TEntity>
{
ValidationState Validate(TEntity entity);
}
We want to be able to return an instance of ValidationState which, obviously, defines the state of validation.
public class ValidationState
{
private readonly ValidationErrorCollection _errors;
public ValidationErrorCollection Errors
{
get
{
return _errors;
}
}
public bool IsValid
{
get
{
return Errors.Count == 0;
}
}
public ValidationState()
{
_errors = new ValidationErrorCollection();
}
}
Notice that we have an strongly typed error collection which we need to define as well. The collection is going to consist of ValidationError objects containing the property name of the entity we're validating and the error message associated with it. This just follows the standard ModelState interface.
public class ValidationErrorCollection : Collection<ValidationError>
{
public void Add(string property, string message)
{
Add(new ValidationError(property, message));
}
}
And here is what the ValidationError looks like:
public class ValidationError
{
private string _property;
private string _message;
public string Property
{
get
{
return _property;
}
private set
{
_property = value;
}
}
public string Message
{
get
{
return _message;
}
private set
{
_message = value;
}
}
public ValidationError(string property, string message)
{
Property = property;
Message = message;
}
}
The rest of this is StructureMap magic. We need to create validation service layer which will locate validation objects and validate our entity. I'd like to define an interface for this, since i want anyone using validation service to be completely unaware of the StructureMap presence. Besides, i think sprinkling ObjectFactory.GetInstance() anywhere besides the bootstrapper logic a bad idea. Keeping it centralized is a good way to insure good maintainability. Anyway, i use the decorator pattern here:
public interface IValidationService
{
ValidationState Validate<TEntity>(TEntity entity);
}
And we finally implement it:
public class ValidationService : IValidationService
{
#region IValidationService Members
public IValidator<TEntity> GetValidatorFor<TEntity>(TEntity entity)
{
return ObjectFactory.GetInstance<IValidator<TEntity>>();
}
public ValidationState Validate<TEntity>(TEntity entity)
{
IValidator<TEntity> validator = GetValidatorFor(entity);
if (validator == null)
{
throw new Exception("Cannot locate validator");
}
return validator.Validate(entity);
}
#endregion
}
I'm going to be using validation service in my controller. We could move it to the service layer and have StructureMap use property injection to inject an instance of controller's ModelState to the service layer, but i don't want the service layer to be coupled with ModelState. What if we decide to use another validation technique? This is why i'd rather put it in the controller. Here is what my controller looks like:
public class PostController : Controller
{
private IEntityService<Post> _service = null;
private IValidationService _validationService = null;
public PostController(IEntityService<Post> service, IValidationService validationService)
{
_service = service;
_validationService = validationService;
}
}
Here i am injecting my service layer and validaton service instances using StructureMap. So, we need to register both in StructureMap registry:
ForRequestedType<IValidationService>()
.TheDefaultIsConcreteType<ValidationService>();
ForRequestedType<IValidator<Post>>()
.TheDefaultIsConcreteType<PostValidator>();
That's it. I don't show how i implement my PostValidator, but it's simply implementing IValidator interface and defining validation logic in the Validate() method. All that's left to do is call your validation service instance to retrieve the validator, call the validate method on your entity and write any errors to ModelState.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "PostId")] Post post)
{
ValidationState vst = _validationService.Validate<Post>(post);
if (!vst.IsValid)
{
foreach (ValidationError error in vst.Errors)
{
this.ModelState.AddModelError(error.Property, error.Message);
}
return View(post);
}
...
}
Hope i helped somebody out with this :)
I used a similar solution involving a generic implementor of IValidationDictionary uses a StringDictionary and then copied the errors from this back into the model state in the controller.
Interface for validationdictionary
public interface IValidationDictionary
{
bool IsValid{get;}
void AddError(string Key, string errorMessage);
StringDictionary errors { get; }
}
Implementation of validation dictionary with no reference to model state or anything else so structuremap can create it easily
public class ValidationDictionary : IValidationDictionary
{
private StringDictionary _errors = new StringDictionary();
#region IValidationDictionary Members
public void AddError(string key, string errorMessage)
{
_errors.Add(key, errorMessage);
}
public bool IsValid
{
get { return (_errors.Count == 0); }
}
public StringDictionary errors
{
get { return _errors; }
}
#endregion
}
Code in the controller to copy the errors from the dictionary into the model state. This would probably be best as an extension function of Controller.
protected void copyValidationDictionaryToModelState()
{
// this copies the errors into viewstate
foreach (DictionaryEntry error in _service.validationdictionary.errors)
{
ModelState.AddModelError((string)error.Key, (string)error.Value);
}
}
thus bootstrapping code is like this
public static void BootstrapStructureMap()
{
// Initialize the static ObjectFactory container
ObjectFactory.Initialize(x =>
{
x.For<IContactRepository>().Use<EntityContactManagerRepository>();
x.For<IValidationDictionary>().Use<ValidationDictionary>();
x.For<IContactManagerService>().Use<ContactManagerService>();
});
}
and code to create controllers is like this
public class IocControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return (Controller)ObjectFactory.GetInstance(controllerType);
}
}
Just a quick query on this. It's helped me out quite a lot so thanks for putting the answer up, but I wondered which namespace TEntity exists in? I see Colletion(TEntity) needs System.Collections.ObjectModel. My file compiles without anything further but I see your TEntity reference highlighted in Blue which suggests it has a class type, mine is Black in Visual Studio. Hope you can help. I'm pretty keen to get this working.
Have you found any way to seperate validation into the service layer at all? My gut tells me that validating in the Controller is a bit smelly but I've looked high and low to find a way to pass validation error messages back to the controller without tightly coupling the service layer to the controller and can't find anything. :(
Again, thanks for the great post!
Lloyd