I am trying to create an INTEGRATION Test using Moq for some code that involves a REST request.
In regular usage, the code would go out and create a Report record and have other impacts in a 3rd party system.
With the Moq test, the Execute call from the RestSharp IRestClient can be substituted for a dummy method that does nothing. For a successful INTEGRATION test, there are 2 requirements: (a) REQUEST xml looks correct (b) RESPONSE json is returned. I'd like to be able to execute most of the code involved in the integration and inspect a local variable from the system under test in the xUnit code assertions. However, I can't seem to access the local variable using Moq, unless I add some code artifacts around testing.
I have created two projects to illustrate. Hope you can point me in the right direction. Perhaps the code needs to be restructured or a new Mock object for the CommandHandler needs to be created?
Thanks!
TEST PROJECT
using Mocking; // System Under Test
using Moq;
using Newtonsoft.Json.Linq;
using RestSharp;
using System.Net;
using System.Threading;
using Xunit;
namespace MockingTest
{
public class UnitTest1
{
[Fact]
public async void SubmitReport_WithPerson_CanProcessSubmitSuccessfully()
{
// ----------------------------------------------------------------------------------
// Arrange
// ----------------------------------------------------------------------------------
Person person = new Person();
person.Name = "Test";
string testRequestXML = GetTestRequestXML(person);
string testResponseXML = "OK";
// Construct the Mock Rest Client. This should allow most of the submission process to be run -
// but the actual Execute to call CMS will not be done - instead the Mock framework will return
// an arbitrary response as defined below.
var mockRestClient = new Mock<IRestClient>();
RestResponse testRestResponse = GetTestRestResponse(System.Net.HttpStatusCode.OK, string.Empty, ResponseStatus.Completed, testResponseXML);
mockRestClient.Setup(rc => rc.Execute(It.IsAny<IRestRequest>()))
.Returns(testRestResponse);
// ----------------------------------------------------------------------------------
// Act
// ----------------------------------------------------------------------------------
Command command = new Command(person);
CancellationToken cancellationToken = new CancellationToken();
CommandHandler commandHandler = new CommandHandler(mockRestClient.Object); // CommandHandler is the "System Under Test"
string result = await commandHandler.Handle(command, cancellationToken);
JToken responseToken = JToken.Parse(result);
string responseXML = responseToken.SelectToken("response").ToString();
string requestXML = responseToken.SelectToken("request").ToString(); // Normally this would not be available.
// ----------------------------------------------------------------------------------
// Assert
// ----------------------------------------------------------------------------------
Assert.Equal(requestXML, testRequestXML); // Handed back in JSON - normally this would not be the case.
Assert.Equal(commandHandler.ReportXMLRequest, testRequestXML); // Handed back in Property - normally this would not be the case.
}
private RestResponse GetTestRestResponse(HttpStatusCode httpStatusCode, string httpErrorMessage, ResponseStatus httpResponseStatus, string responseXML)
{
RestResponse testRestResponse = new RestResponse();
testRestResponse.StatusCode = httpStatusCode;
testRestResponse.ErrorMessage = httpErrorMessage;
testRestResponse.ResponseStatus = httpResponseStatus;
testRestResponse.Content = responseXML;
return testRestResponse;
}
private string GetTestRequestXML(Person person)
{
// Sample XML.
string xml = string.Empty;
xml = xml + "<xml>";
xml = xml + "<report>";
xml = xml + "<status>" + "Initialized" + "</status>";
xml = xml + "<person>" + person.Name + "</person>";
xml = xml + "</report>";
return xml;
}
}
}
SYSTEM UNDER TEST
using Newtonsoft.Json.Linq;
using RestSharp;
using System;
using System.Threading;
using System.Threading.Tasks;
// System Under Test
namespace Mocking
{
public class Person
{
public string Name { get; set; }
}
public class ReportStatus
{
public string Status { get; private set; }
public ReportStatus ()
{
this.Status = "Initialized";
}
}
public class Report
{
public Person Person { get; private set; }
public ReportStatus ReportStatus { get; private set; }
public Report (Person person)
{
Person = person;
ReportStatus = new ReportStatus();
}
}
public class Command
{
public Person Person { get; private set; }
public Command (Person person)
{
this.Person = person;
}
}
public class CommandHandler
{
public string ReportXMLRequest { get; private set; } // Property to permit validation.
private readonly IRestClient RestClient;
//// Using DI to inject infrastructure persistence Repositories - this is the normal call.
//public CommandHandler(IMediator mediator, IReportRepository reportRepository, IIdentityService identityService)
//{
// ReportXMLRequest = string.Empty;
// RestClient = new RestClient();
//}
// MOQ Addition - Overload constructor for Moq Testing.
public CommandHandler(IRestClient restClient)
{
ReportXMLRequest = string.Empty;
RestClient = restClient;
}
public async Task<string> Handle(Command command, CancellationToken cancellationToken)
{
Report report = new Report(command.Person);
string reportResult = Submit(report);
return reportResult;
}
private string Submit(Report report)
{
string responseXML = string.Empty;
string localVariableForRequestXML = GetRequestXML(report);
// MOQ Addition - Set Property to be able to inspect it from the integration test.
this.ReportXMLRequest = localVariableForRequestXML;
IRestClient client = RestClient;
string baseType = client.GetType().BaseType.FullName;
client.BaseUrl = new Uri("http://SampleRestURI");
RestRequest request = new RestRequest(Method.POST);
request.AddParameter("application/xml", localVariableForRequestXML, ParameterType.RequestBody);
// Normally, this REST request would go out and create a Report record and have other impacts in a 3rd party system.
// With Moq, the Execute call from the RestSharp IRestClient can be substituted for a dummy method.
// For a successful INTEGRATION test, there are 2 requirements:
// (a) REQUEST xml looks correct (b) RESPONSE json is returned.
**IRestResponse response = client.Execute(request);**
responseXML = response.Content;
// MOQ Addition - Do something... e.g. return JSON response with extra information.
JObject json = null;
if (baseType.ToLowerInvariant().Contains("moq"))
{
json = new JObject(
new JProperty("response", responseXML),
new JProperty("request", localVariableForRequestXML)
);
}
else
{
json = new JObject(new JProperty("response", responseXML));
}
string jsonResponse = json.ToString();
return jsonResponse;
}
private string GetRequestXML(Report report)
{
// Sample XML - normally this would be quite complex based on Person and other objects.
string xml = string.Empty;
xml = xml + "<xml>";
xml = xml + "<report>";
xml = xml + "<status>" + report.ReportStatus.Status + "</status>";
xml = xml + "<person>" + report.Person.Name + "</person>";
xml = xml + "</report>";
return xml;
}
}
}
Apart from the poorly designed subject and test, (which appears to be more of a unit test than an integration test), the mocked dependency can be used to retrieve the provided input.
You can either use a Callback
//...code removed for brevity
string requestXML = string.Empty;
mockRestClient
.Setup(_ => _.Execute(It.IsAny<IRestRequest>()))
.Callback((IRestRequest request) => {
var parameter = request.Parameters.Where(p => p.Name == "application/xml").FirstOrDefault();
if(parameter != null && parameter.Value != null) {
requestXML = parameter.Value.ToString();
}
})
.Returns(testRestResponse);
//...code removed for brevity
Assert.Equal(requestXML, testRequestXML);
Or do the same directly in the Returns delegate
//...code removed for brevity
string requestXML = string.Empty;
mockRestClient
.Setup(_ => _.Execute(It.IsAny<IRestRequest>()))
.Returns((IRestRequest request) => {
var parameter = request.Parameters.Where(p => p.Name == "application/xml").FirstOrDefault();
if(parameter != null && parameter.Value != null) {
requestXML = parameter.Value.ToString();
}
return testRestResponse;
});
//...code removed for brevity
Assert.Equal(requestXML, testRequestXML);
There is no need to modify the subject under test specifically for the purposes of testing. The injected abstraction should be enough to provided access to desired variables via the mock.
In the commented out constructor of the subject
RestClient = new RestClient(); /<-- don't do this
should not be done as it tightly couples the class to the rest client. There is also no need for the overload. Move the abstraction to the initial constructor. It is already accepting abstractions.
// Using DI to inject infrastructure persistence Repositories - this is the normal call.
public CommandHandler(IMediator mediator, IReportRepository reportRepository,
IIdentityService identityService, IRestClient restClient) {
RestClient = restClient;
//...assign other local variables
}
If the test is meant to be async then have it return a Task and not async void
public async Task SubmitReport_WithPerson_CanProcessSubmitSuccessfully() {
//...
}
But given that the subject appears incomplete, it is not certain that it is actually using async flow as the following method
public async Task<string> Handle(Command command, CancellationToken cancellationToken)
{
Report report = new Report(command.Person);
string reportResult = Submit(report);
return reportResult;
}
contains no awaited methods.
Related
I have a API which connects to my dynamo db. My API has quite a few endpoints for GET, POST, Delete etc. I am using the following code:
var awsCredentials = Helper.AwsCredentials(id, password);
var awsdbClient = Helper.DbClient(awsCredentials, "us-east-2");
var awsContext = Helper.DynamoDbContext(awsdbClient);
List<ScanCondition> conditions = new List<ScanCondition>();
var response = await context.ScanAsync<MyData>(conditions).GetRemainingAsync();
return response.ToList();
The first three lines of my code ie setting awsCredentials, awsdbClient & awsContext are repeated in each of my WEB API call.
And this is my static helper class:
public static class Helper
{
public static BasicAWSCredentials AwsCredentials(string id, string password)
{
var credentials = new BasicAWSCredentials(id, password);
return credentials;
}
public static AmazonDynamoDBClient DynamoDbClient(BasicAWSCredentials credentials, RegionEndpoint region)
{
var client = new DBClient(credentials, region);
return client;
}
public static DynamoDBContext DynamoDbContext(AmazonDynamoDBClient client)
{
var context = new DynamoDBContext(client);
return context;
}
}
I use this helper class in my API to initialize AWS.
Is there a better way to initialize this?
Let's take advantage of ASP.Net's built-in Dependency Injection.
We need to make a quick interface to expose the values you need.
public interface IDynamoDbClientAccessor
{
DynamoDBContext GetContext();
}
And a settings class that we'll use in a bit.
public class DynamoDbClientAccessorSettings
{
public string Id { get; set; }
public string Password { get; set; }
public string Region { get; set; }
}
Now the concrete class.
public class DynamoDbClientAccessor : IDynamoDbClientAccessor
{
private readonly DynamoDbClientAccessorSettings settings;
public DynamoDbClientAccessor(IOptions<DynamoDbClientAccessorSettings> options)
{
settings = options?.Value ?? throw new ArgumentNullException(nameof(options));
}
public DynamoDBContext GetContext()
{
// You have the option to alter this if you don't
// want to create a new context each time.
// Have a private variable at the top of this class
// of type DynamoDBContext. If that variable is not null,
// return the value. If it is null, create a new value,
// set the variable, and return it.
var awsCredentials = Helper.AwsCredentials(settings.Id, settings.Password);
var awsdbClient = Helper.DbClient(awsCredentials, settings.Region);
var awsContext = Helper.DynamoDbContext(awsdbClient);
return awsContext;
}
}
Hook all of this up in your Startup class
services.AddSingleton<IDynamoDbClientAccessor, DynamoDbClientAccessor>();
services.Configure<DynamoDbClientAccessorSettings>(c =>
{
c.Id = "YOUR ID";
c.Password = "YOUR PASSWORD";
c.Region = "YOUR REGION";
});
Now in your controller or other DI service you ask for a IDynamoDbClientAccessor instance in the constructor.
Once you get more familar with Dependency Injection you'll be able to break apart more things into their own dependent services. As Daniel says, the AWS SDK even provides some interfaces for you to use which can help as well.
My unit testing method is as follows
[Test]
public void TrackPublicationChangesOnCDSTest()
{
//Arrange
// objDiskDeliveryBO = new DiskDeliveryBO();
//Act
var actualResult = objDiskDeliveryBO.TrackPublicationChangesOnCDS();
//Assert
var expectedZipName = 0;
Assert.AreEqual(expectedZipName, actualResult);
}
The Actual method TrackPublicationChangesOnCDS in BO is as follows
public int TrackPublicationChangesOnCDS()
{
var resultFlag = -1;
try
{
string pubUpdateFileCDSPath = CommonCalls.PubUpdateFileCDSPath;
string pubUpdateFileLocalPath = CommonCalls.PubUpdateFileLocalPath;
if (File.Exists(pubUpdateFileCDSPath))
File.Copy(pubUpdateFileCDSPath, pubUpdateFileLocalPath, true);
if (File.Exists(pubUpdateFileLocalPath))
{
string[] pubRecords = File.ReadAllLines(pubUpdateFileLocalPath);
var pubRecordsExceptToday = pubRecords.Where(p => !p.Trim().EndsWith(DateTime.Now.ToString("dd/MM/yy"))).ToList();
resultFlag = new DiskDeliveryDAO().TrackPublicationChangesOnCDS(pubRecordsExceptToday);
File.WriteAllText(pubUpdateFileLocalPath, string.Empty);
string[] pubRecordsCDS = File.ReadAllLines(pubUpdateFileCDSPath);
var pubRecordsTodayCDS = pubRecordsCDS.Where(p => p.Trim().EndsWith(DateTime.Now.ToString("dd/MM/yy"))).ToList();
File.WriteAllLines(pubUpdateFileCDSPath, pubRecordsTodayCDS);
}
return resultFlag;
}
catch (Exception)
{
return -1;
}
}
While debugging Debugger comes till
string pubUpdateFileCDSPath = CommonCalls.PubUpdateFileCDSPath;
But CommonCalls.PubUpdateFileCDSPath; return empty string . It should return a file path . when the method is called directly it works fine . It doesn't work when it is called inside a unit testing method.
CommonCalls.PubUpdateFileCDSPath is a static property defined as below .
public static string PubUpdateFileCDSPath
{
get { return GetXmlConfigValue("PubUpdateFileCDSPath"); }
}
public static string GetXmlConfigValue(string nodeName)
{
var xml = new XmlDocument();
xml.Load(ConfigValuesXml);
var node = xml.SelectSingleNode("JanesOfflineDeliveryService/" + nodeName);
return node != null ? node.InnerText : string.Empty;
}
Configvaluesxml is a xml file path . Contents of the file is
<JanesOfflineDeliveryService>
<PubUpdateFileCDSPath>D:\OfflineDelivery\CDS\pub_update.txt</PubUpdateFileCDSPath>
<PubUpdateFileLocalPath>D:\pub_update.txt</PubUpdateFileLocalPath>
</JanesOfflineDeliveryService>
In your test scenario GetXmlConfigValue("PubUpdateFileCDSPath") does not exist, so string empty is returned. Thats why you should avoid static methods, because they are not mockable. A workaround could be to pass the path variables into the method.
Using static dependencies make unit testing code in isolation difficult. invert the dependency by abstracting and injecting them into the dependent class.
public interface ICommonCalls {
string PubUpdateFileCDSPath { get; }
string PubUpdateFileLocalPath { get; }
}
the implementation of the above interface would either wrap the your static calls or better yet just implement them.
The dependent class would be refactored to allow for the dependency inversion.
public class DiskDeliveryBO {
private readonly ICommonCalls CommonCalls;
public DiskDeliveryBO(ICommonCalls common) {
this.CommonCalls = common;
}
//...other code removed for brevity.
}
However the target method also has a lot of tight coupling to implementation concerns like the file system. That too should be abstracted and inverted out of the dependent class.
Since I have converted my WCF methods to Async, my unit tests have failed, and I can't figure out the correct syntax to get them to work.
Cllient proxy class
public interface IClientProxy
{
Task DoSomething(CredentialDataList credentialData, string store);
}
service class
public class CredentialSync : ICredentialSync
{
private ICredentialRepository _repository;
private IClientProxy _client;
public CredentialSync()
{
this._repository = new CredentialRepository();
this._client = new ClientProxy();
}
public CredentialSync(ICredentialRepository repository, IClientProxy client)
{
this._repository = repository;
this._client = client;
}
public async Task Synchronise(string payrollNumber)
{
try
{
if (string.IsNullOrEmpty(payrollNumber))
{
.... some code
}
else
{
CredentialDataList credentialData = new CredentialDataList();
List<CredentialData> credentialList = new List<CredentialData>();
// fetch the record from the database
List<GetCredentialData_Result> data = this._repository.GetCredentialData(payrollNumber);
var pinData = this._repository.GetCredentialPinData(payrollNumber);
// get the stores for this employee
var storeList = data.Where(a => a.StoreNumber != null)
.GroupBy(a => a.StoreNumber)
.Select(x => new Store { StoreNumber = x.Key.ToString() }).ToArray();
var credential = this.ExtractCredentialData(data, pinData, payrollNumber);
credentialList.Add(credential);
credentialData.CredentialList = credentialList;
foreach (var store in storeList)
{
//this line causes an Object reference not set to an instance of an object error
await _client.DoSomething(credentialData, store.StoreNumber);
}
}
}
catch (Exception ex)
{
throw new FaultException<Exception>(ex);
}
}
Test Class
/// </summary>
[TestClass]
public class SynchTest
{
private Mock<ICredentialRepository> _mockRepository;
private Mock<IClientProxy> _mockService;
[TestInitialize]
public void Setup()
{
... some setups for repository which work fine
}
[TestMethod]
public async Task SynchroniseData_WithOneEmployee_CallsReplicateService()
{
this._mockService = new Mock<IClientProxy>();
this._mockService.Setup(x=>x.DoSomething(It.IsAny<CredentialDataList>(), It.IsAny<string>()));
// arrange
string payrollNumber = "1";
CredentialSync service = new CredentialSync(this._mockRepository.Object, this._mockService.Object);
// act
await service.Synchronise(payrollNumber);
// assert
this._mockService.VerifyAll();
}
The error is when ClientProxy.DoSomething is called:
Object reference not set to an instance of an object
The parameters are both fine.
If I convert my ClientProxy.DoSomething method to a synchronous method
(public void DoSomething(...) )the code works fine, but I do need this to be called asynchronously
DoSomething returns null instead of returning a Task, and so you get an exception when awaiting it. You need to specify when building the mock that it should return a Task.
In this case it seems that you can simply return an already completed task using Task.FromResult so the mock setup should look like this:
this._mockService.Setup(...).Returns(Task.FromResult(false));
Beginning with the next version of .Net (4.6) you can use Task.CompletedTask like this:
this._mockService.Setup(...).Returns(Task.CompletedTask);
You can reduce the amount of clutter in the code by using ReturnsAsync
this._mockService.Setup(...).ReturnsAsync(false);
This way you can remove the Task.FromResult part of the code
I think you need to return the Task from the DoSomething mock
this._mockService.Setup(x => x.DoSomething(It.IsAny<CredentialDataList>(), It.IsAny<string>()))
.Returns(Task.FromResult<int>(0));
I have a bit of a perfect storm that's preventing me from testing a class. The class is a RestClient that's wrapping an in-house HttpClient (which I cannot modify). The ExecuteMethod method on the HttpClient is void. It accepts an IHttpMethod, and it modifies this object based on the response from the server. I want to mock out ExecuteMethod so that it modifies the IHttpMethod for me. I'm trying to use Callback to achieve this, but it's not working.
Here's the code that sends the request:
var httpClient = this.httpClientFactory.CreateHttpClient();
httpClient.ExecuteMethod(method);
var response = this.GetResourceResponse<T>(method.ResponseBodyAsStream.AsString());
response.ResponseHeaders = method.ResponseHeaders;
response.Status = method.StatusCode.ToString();
response.StatusCode = (int)method.StatusCode;
return response;
And here's my attempt at mocking:
var mockHttpMethod = new Mock<IHttpMethod>();
mockHttpMethod.Setup(m => m.ResponseBodyAsStream).Returns(new MemoryStream(Encoding.UTF8.GetBytes("foo")));
var modifyHttpMethod = new Action<IHttpMethod>(m =>
{
m = mockHttpMethod.Object;
});
var mockHttpClient = new Mock<IHttpClient>();
mockHttpClient.Setup(c => c.ExecuteMethod(It.IsAny<IHttpMethod>()))
.Callback<IHttpMethod>(modifyHttpMethod);
var mockHttpClientFactory = new Mock<ILegalHoldHttpClientFactory>();
mockHttpClientFactory.Setup(f => f.CreateHttpClient()).Returns(mockHttpClient.Object);
var restClient = new RestClient(mockHttpClientFactory.Object);
When the modifyHttpMethod action is executed, I observe two things, both of which I expect:
The incoming IHttpMethod (m) has the properties I expect it to have.
After assigning the mock object to m, it contains the stubbed values that I setup in my test.
However, after the callback is executed and control is returned to my application code, my method variable still has its old values that I saw in step 1 above, which causes a null reference exception when trying to read method.ResponseBodyAsStream.
Is what I'm trying to do even achievable? If so, how? Thanks!
I've replicated your setup vis a vis mocking, and can't find any issues with it:
public interface IHttpMethod
{
MemoryStream ResponseBodyAsStream { get; set; }
}
public interface IHttpClient
{
void ExecuteMethod(IHttpMethod method);
}
public class HttpClient : IHttpClient
{
#region IHttpClient Members
public void ExecuteMethod(IHttpMethod method)
{
}
#endregion
}
public class Factory
{
public virtual IHttpClient CreateHttpClient()
{
return new HttpClient();
}
}
public class ClassUnderTest
{
private readonly Factory _factory;
public ClassUnderTest(Factory factory)
{
_factory = factory;
}
public string GetResponseAsString(IHttpMethod method)
{
var myClient = _factory.CreateHttpClient();
myClient.ExecuteMethod(method);
return method.ResponseBodyAsStream.ToString();
}
}
[TestClass]
public class ScratchPadTest
{
[TestMethod]
public void SampleTest()
{
var mockHttpMethod = new Mock<IHttpMethod>();
mockHttpMethod.Setup(x => x.ResponseBodyAsStream).Returns(new MemoryStream(Encoding.UTF8.GetBytes("foo")));
var modifyHttpMethod = new Action<IHttpMethod>(m =>
{
m = mockHttpMethod.Object;
});
var mockHttpClient = new Mock<IHttpClient>();
mockHttpClient.Setup(c => c.ExecuteMethod(It.IsAny<IHttpMethod>())).Callback<IHttpMethod>(modifyHttpMethod);
var myFactoryStub = new Mock<Factory>();
myFactoryStub.Setup(f => f.CreateHttpClient()).Returns(mockHttpClient.Object);
var myCut = new ClassUnderTest(myFactoryStub.Object);
Assert.IsNotNull(myCut.GetResponseAsString(mockHttpMethod.Object));
}
}
That test passes, meaning that the memory stream is not null (otherwise an exception would be generated). The only X factor that I can see is your AsString() extension method (I'm assuming that's an extension method as intellisense doesn't show it to me on MemoryStream). Could your problem be in there?
And, by the way, what you're trying to do is almost certainly achievable with Moq.
How can I unit test a method which uses a session object inside of its body?
Let us say I have the following action:
[HttpPost]
public JsonResult GetSearchResultGrid(JqGridParams gridParams, Guid campaignId, string queryItemsString)
{
var queryItems = new JavaScriptSerializer().Deserialize<IList<FilledQueryItem>>(queryItemsString);
IPageData pageData = gridParams.ToPageData();
var extraFieldLinker = SessionHandler.CurrentExtraFieldsLinker;
var searchParams = new SearchParamsModel(extraFieldLinker, queryItems);
IList<CustomerSearchResultRow> searchResults = null;
searchResults = _customerService.SearchCustomersByUrlAndCampaign(campaignId,
searchParams.SearchString,
searchParams.AddressFilterPredicate,
pageData);
return GetGridData<CustomerSearchResultGridDefinition, CustomerSearchResultRow>(searchResults, pageData);
}
I made the following unit tests which fails so far because of the session thing:
[Test]
public void CanGetSearchResultGrid()
{
//Initialize
var mockJqGridParams = new Mock<JqGridParams>();
var mockPageData = new Mock<IPageData>();
IPagedList<CustomerSearchResultRow> mockPagedResult = new PagedList<CustomerSearchResultRow>(mockPageData.Object);
var guid= Guid.NewGuid();
const string searchString =
"[{\"Caption\":\"FirstName\",\"ConditionType\":\"contains\",\"Value\":\"d\",\"NextItem\":\"Last\"}]";
Func<Address,bool> addressFilterPredicate = (x => true);
//Setup
mockJqGridParams.Setup(x => x.ToPageData()).Returns(mockPageData.Object);
_customerService.Setup(x => x.SearchCustomersByUrlAndCampaign(guid, searchString, addressFilterPredicate, mockPageData.Object))
.Returns(mockPagedResult);
//Call
var result = _homeController.GetSearchResultGrid(mockJqGridParams.Object, guid, searchString);
mockJqGridParams.Verify(x => x.ToPageData(), Times.Once());
_customerService.Verify(x => x.SearchCustomersByUrlAndCampaign(guid, searchString, addressFilterPredicate, mockPageData.Object)
, Times.Once());
//Verify
Assert.That(result, Is.Not.Null);
Assert.That(result, Is.TypeOf(typeof(JsonResult)));
}
And the method from the helper of course:
public static ExtraFieldsLinker CurrentExtraFieldsLinker
{
get
{
object extraFieldLinker = GetSessionObject(EXTRA_FIELDS_LINKER);
return extraFieldLinker as ExtraFieldsLinker;
}
set { SetSessionObject(EXTRA_FIELDS_LINKER, value); }
}
I've solved similar issues (use of static data accessors that aren't mock friendly - in particular, HttpContext.Current) by wrapping the access in another object, and accessing it through an interface. You could do something like:
pubic interface ISessionData
{
ExtraFieldsLinker CurrentExtraFieldsLinker { get; set; }
}
public class SessionDataImpl : ISessionData
{
ExtraFieldsLinker CurrentExtraFieldsLinker
{
// Note: this code is somewhat bogus,
// since I think these are methods of your class.
// But it illustrates the point. You'd put all the access here
get { return (ExtraFieldsLinker)GetSessionObject(EXTRA_FIELDS_LINKER); }
set { SetSessionObject(EXTRA_FIELDS_LINKER, value); }
}
}
public class ClassThatContainsYourAction
{
static ClassThatContainsYourAction()
{
SessionData = new SessionDataImpl();
}
public static ISessionData SessionData { get; private set; }
// Making this access very ugly so you don't do it by accident
public void SetSessionDataForUnitTests(ISessionData sessionData)
{
SessionData = sessionData;
}
[HttpPost]
public JsonResult GetSearchResultGrid(JqGridParams gridParams,
Guid campaignId, string queryItemsString)
{
var queryItems = // ...
IPageData pageData = // ...
// Access your shared state only through SessionData
var extraFieldLinker = SessionData.CurrentExtraFieldsLinker;
// ...
}
}
Then your unit test can set the ISessionData instance to a mock object before calling GetSearchResultGrid.
Ideally you'd use a Dependency Injection library at some point, and get rid of the static constructor.
If you can figure out a way to make your ISessionData an instanced object instead of static, even better. Mock object frameworks tend to like to create a new mock type for every test case, and having mocks lying around from previous tests is kind of gross. I believe session state is going to be global to your session anyway, so you might not have to do anything tricky to make a non-static object work.