Unit Testing a Static Method of a Static Class - c#

public static class ApplicationUtils
{
public static bool IsCurrentUserAManager()
{
var username = WindowsIdentity.GetCurrent().Name;
bool inAdmin;
if (username == "AdminUser") {
inAdmin = true;
} else {
inAdmin = false;
}
return inAdmin;
}
}
Above is some code that is used to test if the currently logged in user is an Admin, I want to unit test this section by passing in a different username and test if the result is correct.
I have heard that dependency injection would be the best way to do this. But I have no idea how to dependency inject into a static class and a static method.
Can anyone help me fill out the TestMethod below in order to pass in a username and test the method?
(Not using enterprise)
[TestMethod]
public void IsCurrentUserAManagerTestIsAdmin()
{
}

Refactor your class a little to take an identity as a parameter.
public static class ApplicationUtils
{
public static bool IsUserAManager(IIdentity identity)
{
if (identity == null)
throw new NullReferenceException("identity");
return identity.Name == "AdminUser";
}
}
And Your Test Class using Moq
[TestMethod]
public void IsUserAManagerTestIsAdminReturnsFalse()
{
var mockedIdentity = new Moq.Mock<IIdentity>();
mockedIdentity.Setup(x => x.Name).Returns("notanadmin");
var result = ApplicationUtils.IsUserAManager(mockedIdentity.Object);
Assert.IsFalse(result);
}
[TestMethod]
public void IsUserAManagerTestIsAdminReturnsTrue()
{
var mockedIdentity = new Moq.Mock<IIdentity>();
mockedIdentity.Setup(x => x.Name).Returns("AdminUser");
var result = ApplicationUtils.IsUserAManager(mockedIdentity.Object);
Assert.IsTrue(result);
}

One should try to avoid coupling code to static classes as they are difficult to test.
That said, with your current code as is, it can be refactored to allow certain separations of concerns and a more fluent API via extension methods.
public static class ApplicationUtils {
public static Func<IIdentity> userFactory = () => WindowsIdentity.GetCurrent();
public static IIdentity CurrentUser { get { return userFactory(); } }
public static bool IsManager(this IIdentity identity) {
return identity != null && string.Compare(identity.Name, "AdminUser", true) == 0;
}
public static bool IsAuthenticated(this IIdentity identity) {
return identity != null && identity.IsAuthenticated;
}
}
The following test is used as an example to demonstrate how the above is used.
Moq was used as mocking framework
[TestMethod]
public void IsManager_Should_Return_True_For_AdminUser() {
//Arrange
var name = "AdminUser";
var identity = Mock.Of<IIdentity>(_ => _.Name == name);
ApplicationUtils.userFactory = () => identity;
//Act
var actual = ApplicationUtils.CurrentUser.IsManager();
//Assert
Assert.IsTrue(actual);
}
That done I would now like to suggest you refactor your code to make it more SOLID.
Abstract the functionality of getting the current user out into a service provider.
public interface IIdentityProvider {
IIdentity CurrentUser { get; }
}
Pretty simple with an even simpler implementation.
public class DefaultIdentityProvider : IIdentityProvider {
public IIdentity CurrentUser {
get { return WindowsIdentity.GetCurrent(); }
}
}
If using DI you can now inject the provider into dependent classes that have need to access the current user.
This allows the code to more flexible and maintainable as mocks/stubs of the provider and user can be used for isolated unit tests. The extension methods remain the same as they have very simple concerns.
Here is a simple example of a test for the extension method from earlier.
[TestMethod]
public void IsManager_Should_Return_True_For_AdminUser() {
//Arrange
var name = "AdminUser";
var identity = Mock.Of<IIdentity>(_ => _.Name == name);
var provider = Mock.Of<IIdentityProvider>(_ => _.CurrentUser == identity);
//Act
var actual = provider.CurrentUser.IsManager();
//Assert
Assert.IsTrue(actual);
}
Purely for demonstrative purposes, the test for the IsManager extension method only really needs an IIdentity to be exercised.
Assert.IsTrue(Mock.Of<IIdentity>(_ => _.Name == "AdminUser").IsManager());

When your code is difficult to test, changing the code is a viable option!
In this case, consider having the IsCurrentUserAManager receive the username as an input parameter (and rename it to IsUserAManager to reflect the change in behavior). It would look something like this:
public static class ApplicationUtils
{
public static bool IsUserAManager(string username)
{
bool inAdmin;
if (username == "AdminUser") {
inAdmin = true;
} else {
inAdmin = false;
}
return inAdmin;
}
}
This will allow you to send in different values for testing different scenarios. If however the entire class as it is cannot appear in the UT (due to environment constraints on initialization, for example), consider having just the business logic exported to a seperate non static class and write your UT for that.

public static class ApplicationUtils
{
// this is not testable, because I dont use DI
public static bool IsCurrentUserAManager() => TestableMethod(WindowsIdentity.GetCurrent().Name);
// This is testable because it contains logics (if-else)
public static bool TestableMethod(string username) => username == "AdminUser";
}
[TestMethod]
public void IsCurrentUserAManagerTestIsAdmin()
{
Assert.IsTrue(ApplicationUtils.TestableMethod("AdminUser"));
Assert.IsFalse(ApplicationUtils.TestableMethod("adminuser"));
}

Related

Override Autofixture customization setup

I've a class with several services injected in its constructor. I'm using Autofixture with xUnit.net and NSubstitute, and created an attribute to setup the global customization.
public class AutoDbDataAttribute : AutoDataAttribute
{
public AutoDbDataAttribute() : base(() => new Fixture().Customize(new AutoNSubstituteCustomization()))
{
}
public AutoDbDataAttribute(Type customizationType) : base(() =>
{
var customization = Activator.CreateInstance(customizationType) as ICustomization;
var fixture = new Fixture();
fixture.Customize(new AutoNSubstituteCustomization());
fixture.Customize(customization);
return fixture;
})
{
}
}
I also have a custom customization class that setups the common customization for the test methods in the same class.
public class RevenueProviderCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Register<IRevenueContextService>(() =>
{
var contextService = Substitute.For<IRevenueContextService>();
contextService.GetContext().Returns(fixture.Create<RevenueContext>());
return contextService;
});
fixture.Register<ICompanyService>(() =>
{
var companyService = Substitute.For<ICompanyService>();
companyService.Get(Arg.Any<Guid>()).Returns(fixture.Create<Company>());
return companyService;
});
}
}
Now, some of my tests depend on modifying specific properties in the objects returned by the services. So in some cases, I want to modify the RevenueContext and in some cases, I want to modify the Company data.
What I did was creating another object inside the test itself and modify the Returns of the service with the new object, like this:
[Theory]
[AutoDbData(typeof(RevenueProviderCustomization))]
public void ShouldReturnCompanyRevenue(RevenueProvider sut, Company company, [Frozen]IRevenueContextService contextService)
{
var fixture = new Fixture();
RevenueContext context = fixture.Build<RevenueContext>().With(c => c.DepartmentId, null).Create();
contextService.GetContext().Returns(context);
sut.GetRevenue().Should().Be(company.Revenue);
}
But this doesn't work. The RevenueContext from the RevenueProviderCustomization is still used.
Does anyone know how I can override the return from the service? I don't want to setup the fixture one by one in my test, so I was hoping to be able to create a 'general setup' and modify as needed according to the test case.
UPDATE 1
Trying the answer from Mark, I changed the test to
[Theory]
[AutoDbData(typeof(RevenueProviderCustomization))]
public void ShouldReturnCompanyRevenue([Frozen]IRevenueContextService contextService, [Frozen]Company company, RevenueProvider sut, RevenueContext context)
{
context.DepartmentId = null;
contextService.GetContext().Returns(context);
sut.GetRevenue().Should().Be(company.Revenue);
}
The problem is because the RevenueContext is called in the RevenueProvider constructor. So my modification to the DepartmentId happens after the call was made.
public RevenueProvider(IRevenueContextService contextService, ICompanyService companyService)
{
_contextService = contextService;
_companyService = companyService;
_company = GetCompany();
}
public double GetRevenue()
{
if (_hasDepartmentContext)
return _company.Departments.Single(d => d.Id == _departmentId).Revenue;
else
return _company.Revenue;
}
private Company GetCompany()
{
RevenueContext context = _contextService.GetContext();
if (context.DepartmentId.HasValue)
{
_hasDepartmentContext = true;
_departmentId = context.DepartmentId.Value;
}
return _companyService.Get(context.CompanyId);
}
Assuming that RevenueProvider essentially looks like this:
public class RevenueProvider
{
private readonly ICompanyService companySvc;
public RevenueProvider(ICompanyService companySvc)
{
this.companySvc = companySvc;
}
public object GetRevenue()
{
var company = this.companySvc.Get(Guid.Empty);
return company.Revenue;
}
}
Then the following test passes:
[Theory]
[AutoDbData(typeof(RevenueProviderCustomization))]
public void ShouldReturnCompanyRevenue(
[Frozen]ICompanyService companySvc,
RevenueProvider sut,
Company company)
{
companySvc.Get(Arg.Any<Guid>()).Returns(company);
var actual = sut.GetRevenue();
Assert.Equal(company.Revenue, actual);
}
This scenario is exactly what the [Frozen] attribute is designed to handle. The various attributes that AutoFixture defines are applied in the order of the arguments. This is by design, because it enables you to pull out a few values from the argument list before you freeze a type.
In the OP, [Frozen] is only applied after sut, which is the reason the configuration of the mock doesn't apply within the SUT.

Mocking Hangfire RecurringJob Dependency in .Net Core 2

Consider the following controller:
public class SubmissionController : Controller
{
public SubmissionController()
{ }
public IActionResult Post()
{
RecurringJob.AddOrUpdate(() => InitiateSubmission(), Cron.Minutely);
return Ok("Periodic submission triggered");
}
}
Does Hangfire offer an abstraction inject a dependency for RecurringJob class? I have done some research and the only available abstraction is IBackgroundJobClient, which does not have the option to schedule a recurring job.
I need to verify that the job has been added in a unit test.
If you check the source code of RecurringJob class, you will see that its static methods result in call to RecurringJobManager class:
public static class RecurringJob
{
private static readonly Lazy<RecurringJobManager> Instance = new Lazy<RecurringJobManager>(
() => new RecurringJobManager());
// ...
public static void AddOrUpdate(
Expression<Action> methodCall,
string cronExpression,
TimeZoneInfo timeZone = null,
string queue = EnqueuedState.DefaultQueue)
{
var job = Job.FromExpression(methodCall);
var id = GetRecurringJobId(job);
Instance.Value.AddOrUpdate(id, job, cronExpression, timeZone ?? TimeZoneInfo.Utc, queue);
}
// ...
}
RecurringJobManager implements IRecurringJobManager interface which you could use for dependency injection and mock in UT.
However RecurringJob has internal logic for getting a job from lambda and building a job id:
var job = Job.FromExpression(methodCall);
var id = GetRecurringJobId(job);
Job.FromExpression() is a public method that you can safely use. However GetRecurringJobId is a private method defined as following:
private static string GetRecurringJobId(Job job)
{
return $"{job.Type.ToGenericTypeString()}.{job.Method.Name}";
}
GetRecurringJobId basically returns name of job method in form of SubmissionController.InitiateSubmission. It's based on internal class TypeExtensions with extension methods for Type. You can't use this class directly since it is internal, so you should duplicate that logic.
If you follow this approach your final solution would be:
TypeExtensions (copied from Hangfire sources):
static class TypeExtensions
{
public static string ToGenericTypeString(this Type type)
{
if (!type.GetTypeInfo().IsGenericType)
{
return type.GetFullNameWithoutNamespace()
.ReplacePlusWithDotInNestedTypeName();
}
return type.GetGenericTypeDefinition()
.GetFullNameWithoutNamespace()
.ReplacePlusWithDotInNestedTypeName()
.ReplaceGenericParametersInGenericTypeName(type);
}
private static string GetFullNameWithoutNamespace(this Type type)
{
if (type.IsGenericParameter)
{
return type.Name;
}
const int dotLength = 1;
// ReSharper disable once PossibleNullReferenceException
return !String.IsNullOrEmpty(type.Namespace)
? type.FullName.Substring(type.Namespace.Length + dotLength)
: type.FullName;
}
private static string ReplacePlusWithDotInNestedTypeName(this string typeName)
{
return typeName.Replace('+', '.');
}
private static string ReplaceGenericParametersInGenericTypeName(this string typeName, Type type)
{
var genericArguments = type.GetTypeInfo().GetAllGenericArguments();
const string regexForGenericArguments = #"`[1-9]\d*";
var rgx = new Regex(regexForGenericArguments);
typeName = rgx.Replace(typeName, match =>
{
var currentGenericArgumentNumbers = int.Parse(match.Value.Substring(1));
var currentArguments = string.Join(",", genericArguments.Take(currentGenericArgumentNumbers).Select(ToGenericTypeString));
genericArguments = genericArguments.Skip(currentGenericArgumentNumbers).ToArray();
return string.Concat("<", currentArguments, ">");
});
return typeName;
}
public static Type[] GetAllGenericArguments(this TypeInfo type)
{
return type.GenericTypeArguments.Length > 0 ? type.GenericTypeArguments : type.GenericTypeParameters;
}
}
RecurringJobManagerExtensions:
public static class RecurringJobManagerExtensions
{
public static void AddOrUpdate(this IRecurringJobManager manager, Expression<Action> methodCall, Func<string> cronExpression, TimeZoneInfo timeZone = null, string queue = EnqueuedState.DefaultQueue)
{
var job = Job.FromExpression(methodCall);
var id = $"{job.Type.ToGenericTypeString()}.{job.Method.Name}";
manager.AddOrUpdate(id, job, cronExpression(), timeZone ?? TimeZoneInfo.Utc, queue);
}
}
Controller with injected IRecurringJobManager:
public class SubmissionController : Controller
{
private readonly IRecurringJobManager recurringJobManager;
public SubmissionController(IRecurringJobManager recurringJobManager)
{
this.recurringJobManager = recurringJobManager;
}
public IActionResult Post()
{
recurringJobManager.AddOrUpdate(() => InitiateSubmission(), Cron.Minutely);
return Ok("Periodic submission triggered");
}
public void InitiateSubmission()
{
// ...
}
}
Well, this approach will work, but I'm not a fan of it. It's based on some internal Hangfire stuff that could be changed in the future.
That's why I suggest to use another approach. You could add new facade interface (e.g. IRecurringJobFacade) which will mimic methods from RecurringJob that you are going to use. Implementation of this interface will just call corresponding RecurringJob methods. Then you inject this IRecurringJobFacade into the controller and could easily mock it in UT. Here is a sample:
IRecurringJobFacade:
public interface IRecurringJobFacade
{
void AddOrUpdate(Expression<Action> methodCall, Func<string> cronExpression);
// Mimic other methods from RecurringJob that you are going to use.
// ...
}
RecurringJobFacade:
public class RecurringJobFacade : IRecurringJobFacade
{
public void AddOrUpdate(Expression<Action> methodCall, Func<string> cronExpression)
{
RecurringJob.AddOrUpdate(methodCall, cronExpression);
}
}
Controller with injected IRecurringJobFacade:
public class SubmissionController : Controller
{
private readonly IRecurringJobFacade recurringJobFacade;
public SubmissionController(IRecurringJobFacade recurringJobFacade)
{
this.recurringJobFacade = recurringJobFacade;
}
public IActionResult Post()
{
recurringJobFacade.AddOrUpdate(() => InitiateSubmission(), Cron.Minutely);
return Ok("Periodic submission triggered");
}
public void InitiateSubmission()
{
// ...
}
}
As you see this approach is much simpler and most importantly it's much more reliable, since it does not dig into Hangfire internals and just calls RecurringJob methods as usual.
Such facade interface is often used when code could not be mocked directly (static methods or classes not based on interfaces). Some other examples that I have used in my practice: mock of System.IO.File, DateTime.Now, System.Timers.Timer, etc.
I had a similar case with: RecurringJob.RemoveIfExists. I try this (I see the original code in github and setup my mock's):
private void SetupHangfire()
{
Mock<JobStorage> _jobStorageMock = new Mock<JobStorage>();
Mock<IStorageConnection> _storageConnectionMock = new Mock<IStorageConnection>();
Mock<IWriteOnlyTransaction> _transactionConnectionMock = new Mock<IWriteOnlyTransaction>();
JobStorage.Current = _jobStorageMock.Object;
_jobStorageMock
.Setup(y => y.GetConnection())
.Returns(_storageConnectionMock.Object);
_storageConnectionMock
.Setup(y => y.AcquireDistributedLock(It.IsAny<string>(), It.IsAny<TimeSpan>()))
.Returns(_transactionConnectionMock.Object);
_storageConnectionMock
.Setup(y => y.CreateWriteTransaction())
.Returns(_transactionConnectionMock.Object);
_transactionConnectionMock
.Setup(y => y.RemoveHash(It.IsAny<string>()));
_transactionConnectionMock
.Setup(y => y.RemoveFromSet(It.IsAny<string>(), It.IsAny<string>()));
_transactionConnectionMock
.Setup(y => y.Commit());
}

Mock session object with custom extension method

In my asp.net core 2 application, I have a static class which handles some interactions with the session. For example :
public static class BookManager
{
private const string Key = "Books";
public static HttpContextAccessor ContextAccessor => new HttpContextAccessor();
public static void AddBooksToContainer(IEnumerable<BookViewModel> books)
{
ContextAccessor.HttpContext.Session.Set(Key, books);
}
public static IEnumerable<BookViewModel> GetBooks()
{
return ContextAccessor.HttpContext.Session.Get<IEnumerable<BookViewModel>>(Key);
}
}
I have also added some extension methods to the session object :
public static class SessionExtensions
{
public static void Set<T>(this ISession session, string key, IEnumerable<T> value)
{
session.SetString(key, JsonConvert.SerializeObject(value));
}
public static void Set<T>(this ISession session, string key, T value)
{
session.SetString(key, JsonConvert.SerializeObject(value));
}
public static T Get<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default(T) :
JsonConvert.DeserializeObject<T>(value);
}
}
Update: This is how i try to mock :
var bookList = new List<BookViewModel>()
{
new BookViewModel {Author = "test", Id = Guid.NewGuid(), InStock = 1, Price = 1000, Title = "tes"}
};
var httpContextAccessor = new Mock<IHttpContextAccessor>().SetupAllProperties();
httpContextAccessor.Setup(x => x.HttpContext.Session.Get<IEnumerable<BookViewModel>>("test")).Returns(bookList);
And the error i get :
System.NotSupportedException : Invalid setup on an extension method: x =>
x.HttpContext.Session.Get<IEnumerable`1>("test")
The problem is I have tried many ways to mock session and its extension methods but I got no result yet. many ways work in .netframework and not core.
I use xunit for testing and Moq library to mock the objects. Please tell me how can I mock session and its extension methods. Appreciate it.
The problem is not related to .NET Core, is a limitation of the mocking library, you can't mock extension methods with Moq.
This might help: Mocking Extension Methods with Moq
Depend on abstractions and not concretions. Create a Factory method that will provide you with the IHttpContextAccessor abstraction.
public static class BookManager {
private const string Key = "Books";
public static Func<IHttpContextAccessor> ContextAccessor = () => new HttpContextAccessor();
public static void AddBooksToContainer(IEnumerable<BookViewModel> books) {
ContextAccessor().HttpContext.Session.Set(Key, books);
}
public static IEnumerable<BookViewModel> GetBooks() {
return ContextAccessor().HttpContext.Session.Get<IEnumerable<BookViewModel>>(Key);
}
}
This will allow you to replace the abstraction when testing with a mock.
//Arrange
var mock = mock.Of<IHttpContextAccessor>();
BookManager.ContextAccessor = () => {
return mock.Object;
};
//...
Better yet if this manager only has need of the session then do not expose more than it needs.
public static class BookManager {
private const string Key = "Books";
public static Func<ISession> Session = () => new HttpContextAccessor().HttpContext.Session;
public static void AddBooksToContainer(IEnumerable<BookViewModel> books) {
Session().Set(Key, books);
}
public static IEnumerable<BookViewModel> GetBooks() {
return Session().Get<IEnumerable<BookViewModel>>(Key);
}
}
So now only the explicit dependencies need to be mocked for tests.
According to ISession Extensions source code GetString and SetString are also extension methods
You will need to mock ISession.TryGetValue and ISession.Set
//Arrange
var mock = Mock.Of<ISession>();
BookManager.Session = () => {
return mock;
};
var value = Encoding.UTF8.GetBytes("[{some books json strings here}]");
Mock.Get(mock).Setup(_ => _.TryGetValue("Books", out value))
.Returns(true);
//Act
var books = BookManager.GetBooks();
//...

MVC Mock HttpContextBase which is used in a helper

I am using a helper in my controllers and in my views that I have found somewhere on the internet. The helper is called like this in my controller "Url.SiteRoot();"
How can I get my controller to not throw an Exception whenever the helper is called? I am using MVCContrib and moq for my unit tests.
I am thinking of implementing some kind of a check in the helper but it feel like the MVCContrib framework or the moq should be able to handle this so that I don't need to add Exception code in my helpers just to be able to pass the unit tests.
You can see the Helper code here:-
namespace System.Web.Mvc {
public static class UrlHelpers {
public static string SiteRoot(HttpContextBase context) {
return SiteRoot(context, true);
}
public static string SiteRoot(HttpContextBase context, bool usePort) {
var Port = context.Request.ServerVariables["SERVER_PORT"];
if (usePort) {
if (Port == null || Port == "80" || Port == "443")
Port = "";
else
Port = ":" + Port;
}
var Protocol = context.Request.ServerVariables["SERVER_PORT_SECURE"];
if (Protocol == null || Protocol == "0")
Protocol = "http://";
else
Protocol = "https://";
var appPath = context.Request.ApplicationPath;
if (appPath == "/")
appPath = "";
var sOut = Protocol + context.Request.ServerVariables["SERVER_NAME"] + Port + appPath;
return sOut;
}
public static string SiteRoot(this UrlHelper url) {
return SiteRoot(url.RequestContext.HttpContext);
}
public static string SiteRoot(this ViewPage pg) {
return SiteRoot(pg.ViewContext.HttpContext);
}
public static string SiteRoot(this ViewUserControl pg) {
var vpage = pg.Page as ViewPage;
return SiteRoot(vpage.ViewContext.HttpContext);
}
public static string SiteRoot(this ViewMasterPage pg) {
return SiteRoot(pg.ViewContext.HttpContext);
}
public static string GetReturnUrl(HttpContextBase context) {
var returnUrl = "";
if (context.Request.QueryString["ReturnUrl"] != null) {
returnUrl = context.Request.QueryString["ReturnUrl"];
}
return returnUrl;
}
public static string GetReturnUrl(this UrlHelper helper) {
return GetReturnUrl(helper.RequestContext.HttpContext);
}
public static string GetReturnUrl(this ViewPage pg) {
return GetReturnUrl(pg.ViewContext.HttpContext);
}
public static string GetReturnUrl(this ViewMasterPage pg) {
return GetReturnUrl(pg.Page as ViewPage);
}
public static string GetReturnUrl(this ViewUserControl pg) {
return GetReturnUrl(pg.Page as ViewPage);
}
}
}
As #Jeremy Frey writes you're getting the exceptions because you're failing to stub/fake some essential parts of the HttpContext.
How about using:
Request.Url.GetLeftPart(System.UriPartial.Authority)
instead of trying to build the logic for building the url yourself? If I remember correctly it should pick up the protocol and port correctly, as well as any virtual directory, site, etc.
You've probably realized that the reason you're getting exceptions from your extension methods is due to unimplemented properties or methods on the mocked objects, e.g. Request on HttpContextBase, or RequestContext on UrlHelper.
Take a look at some of the strategies posted here for ideas on how to mock the extension method calls. I personally prefer this strategy, which would have you refactoring your extension methods to bet swappable at runtime.
For example, instead of:
public static class UrlHelperExtensions
{
public static string GetReturnUrl(this UrlHelper helper)
{
return // your implementation of GetReturnUrl here
}
}
You'd have:
public interface IUrlHelperExtensions
{
string GetReturnUrl(UrlHelper helper);
}
public static class UrlHelperExtensions
{
public static IUrlHelperExtensions Extensions(this UrlHelper target)
{
return UrlHelperExtensionFactory(target);
}
static UrlExtensions
{
UrlHelperExtensionFactory = () => new DefaultUrlHelperExtensionStrategy();
}
public static Func UrlHelperExtensionFactory { get; set; }
}
public DefaultUrlHelperExtensionStrategy : IUrlHelperExtensions
{
public string GetReturnUrl(UrlHelper helper)
{
return // your implementation of GetReturnUrl here
}
}
You'd need to change the way you'd call your extension methods, from urlHelper.GetReturnUrl() to urlHelper.Extensions().GetReturnUrl(), and during unit testing, you can set UrlHelperExtensions.UrlHelperExtensionFactory to a mocked object, but this way, you can control the extension methods' behavior at test time.
That code looks a little complicated. I think this does the same thing and would be much easier to test. (I'm not sure why it needs to be though.)
public string FullApplicationPath(HttpRequestBase request)
{
var path = request.Url.AbsoluteUri.Replace(request.Url.AbsolutePath,string.Empty);
if (!string.IsNullOrEmpty(request.Url.Query))
{
path = path.Replace(request.Url.Query, string.Empty);
}
return path + request.ApplicationPath;
}

How can I get StructureMap's AutoMocker to mock fake data?

I'm currently trying to implement StructureMap's AutoMocking functionality and I need help with getting the mocked .
I have a Test method as follows:
[Test]
public void DirctoryResult_Returns_Groups()
{
var autoMocker = new RhinoAutoMocker<GroupController>(MockMode.AAA);
GroupController controller = autoMocker.ClassUnderTest;
var directoryResult = controller.DirectoryResult("b");
var fundDirectoryViewModel = (FundDirectoryViewModel)directoryResult.ViewData.Model;
Assert.IsNotNull(fundDirectoryViewModel.Groups);
}
Currently the test is failing because fundDirectoryViewModel.Groups is null.
The real implementation of DirectoryResult is as follows:
private readonly IGroupService _groupService;
public PartialViewResult DirectoryResult(string query)
{
return PartialView(new FundDirectoryViewModel
{
Groups =_groupService.GetGroupsByQuery(query)
});
}
where _groupService.GetGroupsByQuery(query) uses an interface to IGroupRepository to read data from the database. Of course, I don't want my test to read data from the actual database, but can somebody tell me how to get mock data for it?
What do I need to do to get the AutoMocker to mock the fake data for me?
update:
for reference, this is the definition of GroupService & GroupRepository
public class GroupService : IGroupService
{
private readonly IGroupRepository _groupRepository;
public GroupService(IGroupRepository groupRepository)
{
_groupRepository = groupRepository;
}
public IList<CompanyGroupInfo> GetGroupsByQuery(string query)
{
return _groupRepository.GetGroupsByQuery(query);
}
}
public class GroupRepository : DataUniverseRepository, IGroupRepository
{
public GroupRepository(ISession session)
{
_session = session;
}
public IList<CompanyGroupInfo> GetGroupsByQuery(string query)
{
// dig into the database and return stuff with _session..
}
}
I've been informed that the question was wrong. Automocker doesn't mock data like that. It's up to me to specify the fake data with Rhino Mocks.
This works:
[Test]
public void DirctoryResult_Returns_Groups()
{
var service = autoMocker.Get<IGroupService>();
service.Expect(srv => srv.GetGroupsByQuery(Arg<string>.Is.Anything))
.Return(new List<CompanyGroupInfo>
{
new CompanyGroupInfo(),
new CompanyGroupInfo(),
new CompanyGroupInfo()
});
service.Replay();
var directoryResult = _controller.DirectoryResult("b");
var fundDirectoryViewModel = (FundDirectoryViewModel)directoryResult.ViewData.Model;
Assert.That(fundDirectoryViewModel.Groups.Count, Is.EqualTo(3));
service.AssertWasCalled(srv => srv.GetGroupsByQuery(Arg<string>.Is.Equal("b")));
}

Categories