Moq returns null and BadRequestObjectResult - c#

I'm new to unit testing, and trying to mock postContinent. But gives null and BadRequestObjectResult.
ContinentControllerTests
public class ContinentControllerTests {
// RepoMocks
private readonly Mock<IContinentRepository> _continentRepoMock = new Mock<IContinentRepository>();
private readonly Mock<ICountryRepository> _countryRepoMock = new Mock<ICountryRepository>();
private readonly Mock<ICityRepository> _cityRepoMock = new Mock<ICityRepository>();
// Controller
private readonly ContinentController _continentController;
public ContinentControllerTests() {
_continentServiceMock = new ContinentService(_continentRepoMock.Object);
_continentController = new ContinentController(new ContinentService(_continentRepoMock.Object), new CountryService(_countryRepoMock.Object), new CityService(_cityRepoMock.Object));
}
[Fact]
public void PostContinent_ValidInput_ReturnsCreateAtAction() {
// Arrange
_continentRepoMock
.Setup(repo => repo.HeeftContinent("Test"))
.Returns(false);
_continentRepoMock
.Setup(repo => repo.BestaatContinent(new Continent("Test", new List<Country>())))
.Returns(false);
_continentRepoMock
.Setup(repo => repo.VoegContinentToe(new Continent("Test", new List<Country>())))
.Returns(new Continent(1, "Test", new List<Country>()));
// Act
var response = _continentController.PostContinent(new ContinentInputDTO { Name = "Test" });
// Assert
Assert.IsType<CreatedAtActionResult>(response.Result);
}
}
ContinentController
public class ContinentController : ControllerBase {
private string _hostURL = $"http://localhost:5000/api/continent";
private string _riverURL = $"http://localhost:5000/api/river";
private ContinentService _continentService;
private CountryService _countryService;
private CityService _cityService;
public ContinentController(ContinentService continentService, CountryService countryService, CityService cityService) {
_continentService = continentService;
_countryService = countryService;
_cityService = cityService;
}
[HttpPost]
public ActionResult<ContinentOutputDTO> PostContinent([FromBody] ContinentInputDTO continentDto) {
try {
if (_continentService.HeeftContinent(continentDto.Name)) { return BadRequest("Continent naam moet unique zijn!"); }
var mappedContinent = MapToDomain.MapToContinentDomain(continentDto);
Continent continent = _continentService.VoegContinentToe(mappedContinent);
return CreatedAtAction(nameof(GetContinent), new { continentId = continent.Id },
MapFromDomain.MapFromContinentDomain(_hostURL, continent));
}
catch (Exception ex) { return BadRequest(ex.Message); }
}
}
ContinentService
public class ContinentService {
private readonly IContinentRepository _repo;
public ContinentService(IContinentRepository repo) { _repo = repo;}
public Continent VoegContinentToe(Continent c) {
if (c == null) throw new ContinentServiceException("VoegContinentToe : continent is null");
if (_repo.BestaatContinent(c)) throw new ContinentServiceException("VoegContinentToe : continent bestaat reeds");
try {return _repo.VoegContinentToe(c);}
catch (Exception ex) { throw new ContinentServiceException("VoegContinentToe: ", ex);}
}
}
Error:
Message: 
Assert.IsType() Failure
Expected: Microsoft.AspNetCore.Mvc.CreatedAtActionResult
Actual: Microsoft.AspNetCore.Mvc.BadRequestObjectResult

The problem is in your Setup function. Reference types are equal only if you have overridden the equals function or if the are the exact same reference.
So by setting it up with the new keyword, it will never match the execution time object.
Try the It.IsAny<T> function from MOQ to verify.
Check the example here: https://documentation.help/Moq/3CF54A74.htm
// Throws an exception for a call to Remove with any string value.
mock.Setup(x => x.Remove(It.IsAny<string>())).Throws(new InvalidOperationException());
Example. Apply to all setups.
_continentRepoMock
.Setup(repo => repo.BestaatContinent(It.IsAny<Continent>()))
.Returns(false);

Related

Unable to cast object of type 'System.Linq.EnumerableQuery` to type 'MongoDB.Driver.Linq.IMongoQueryable`

I have a class SendNotificationsToSubscribersCommandHandler with the following Method Handle and I have test method GetAllSubscriptionsWithCorrectParamsProductId() to check that Handle method returns correct List.
I get following error:
Message:
Test method Grand.Services.Tests.Commands.Handlers.Catalog.SendNotificationsToSubscribersCommandHandlerTest.GetAllSubscriptionsWithCorrectParamsProductId threw exception:
System.InvalidCastException: Unable to cast object of type 'System.Linq.EnumerableQuery`1[Grand.Domain.Catalog.BackInStockSubscription]' to type 'MongoDB.Driver.Linq.IMongoQueryable`1[Grand.Domain.Catalog.BackInStockSubscription]'.
Stack Trace:
MongoQueryable.Where[TSource](IMongoQueryable`1 source, Expression`1 predicate)
SendNotificationsToSubscribersCommandHandler
public class SendNotificationsToSubscribersCommandHandler : IRequestHandler<SendNotificationsToSubscribersCommand, IList<BackInStockSubscription>>
{
private readonly ICustomerService _customerService;
private readonly IWorkflowMessageService _workflowMessageService;
private readonly IRepository<BackInStockSubscription> _backInStockSubscriptionRepository;
public SendNotificationsToSubscribersCommandHandler(
ICustomerService customerService,
IWorkflowMessageService workflowMessageService,
IRepository<BackInStockSubscription> backInStockSubscriptionRepository)
{
_customerService = customerService;
_workflowMessageService = workflowMessageService;
_backInStockSubscriptionRepository = backInStockSubscriptionRepository;
}
public async Task<IList<BackInStockSubscription>> Handle(SendNotificationsToSubscribersCommand request, CancellationToken cancellationToken)
{
if (request.Product == null)
throw new ArgumentNullException("product");
int result = 0;
var query = _backInStockSubscriptionRepository.Table;
//product
query = query.Where(biss => biss.ProductId == request.Product.Id);
//warehouse
if (!string.IsNullOrEmpty(request.Warehouse))
query = query.Where(biss => biss.WarehouseId == request.Warehouse);
//warehouse
if (!string.IsNullOrEmpty(request.AttributeXml))
query = query.Where(biss => biss.AttributeXml == request.AttributeXml);
query = query.OrderByDescending(biss => biss.CreatedOnUtc);
var subscriptions = await query.ToListAsync();
//var subscriptions = await GetAllSubscriptionsByProductId(request.Product.Id, request.AttributeXml, request.Warehouse);
foreach (var subscription in subscriptions)
{
var customer = await _customerService.GetCustomerById(subscription.CustomerId);
//ensure that customer is registered (simple and fast way)
if (customer != null && CommonHelper.IsValidEmail(customer.Email))
{
var customerLanguageId = customer.GetAttributeFromEntity<string>(SystemCustomerAttributeNames.LanguageId, subscription.StoreId);
await _workflowMessageService.SendBackInStockNotification(customer, request.Product, subscription, customerLanguageId);
result++;
}
}
return subscriptions;
}
}
My test method:
[TestClass()]
public class SendNotificationsToSubscribersCommandHandlerTest
{
private Mock<ICustomerService> _mockCustomerService;
private Mock<IRepository<BackInStockSubscription>> _mockBackInStockSubscriptionRepository;
private Mock<IMongoQueryable<BackInStockSubscription>> _mongoQueryableMock;
private IQueryable<BackInStockSubscription> _expectedQueryable;
private List<BackInStockSubscription> _expected;
private Mock<IWorkflowMessageService> _mockWorkflowMessageService;
private SendNotificationsToSubscribersCommandHandler _handler;
private SendNotificationsToSubscribersCommand _sendNotificationsToSubscribersCommand;
[TestInitialize()]
public void Init()
{
_expected = new List<BackInStockSubscription>
{
new BackInStockSubscription { WarehouseId = "11", ProductId = "11" },
new BackInStockSubscription { WarehouseId = "11", ProductId = "11" }
};
_mockCustomerService = new Mock<ICustomerService>();
_mockBackInStockSubscriptionRepository = new Mock<IRepository<BackInStockSubscription>>();
_expectedQueryable = _expected.AsQueryable();
_mongoQueryableMock = new Mock<IMongoQueryable<BackInStockSubscription>>();
_mongoQueryableMock.As<IEnumerable<BackInStockSubscription>>();
_mongoQueryableMock.Setup(x => x.ElementType).Returns(_expectedQueryable.ElementType);
_mongoQueryableMock.Setup(x => x.Expression).Returns(_expectedQueryable.Expression);
_mongoQueryableMock.Setup(x => x.Provider).Returns(_expectedQueryable.Provider);
_mongoQueryableMock.Setup(x => x.GetEnumerator()).Returns(_expectedQueryable.GetEnumerator());
_mockBackInStockSubscriptionRepository.Setup(x => x.Table).Returns(_mongoQueryableMock.Object);
_mockWorkflowMessageService = new Mock<IWorkflowMessageService>();
_sendNotificationsToSubscribersCommand = new SendNotificationsToSubscribersCommand { Product = new Product { Id = "11"}, Warehouse = "11" };
_handler = new SendNotificationsToSubscribersCommandHandler(_mockCustomerService.Object, _mockWorkflowMessageService.Object, _mockBackInStockSubscriptionRepository.Object);
}
[TestMethod()]
public async Task GetAllSubscriptionsWithCorrectParamsProductId()
{
var result = await _handler.Handle(_sendNotificationsToSubscribersCommand, default);
var resultList = result.ToList();
Assert.AreEqual(resultList, _expected);
}
}
I get error on line
query = query.Where(biss => biss.ProductId == productId);
UPDATE
When I debug application, property _backInStockSubscriptionRepository.Table has Expression.Value = {aggregate([])}
If I debug test method property Table of Mock object has Expression.Value with Value = System.Collections.Generic.List<Grand.Domain.Catalog.BackInStockSubscription> with my two objects.
Any help is greatly appreciated
Take a look at here: How to mock a class that implements multiple interfaces
Try something like this:
_mongoQueryableMock = new Mock<IMongoQueryable<BackInStockSubscription>>();
_mongoQueryableMock.As<IEnumerable<BackInStockSubscription>>();
...

GraphQL-dotnet fields Validation Prior Resolving

I'm building GraphQL API in Asp.Net Core 2.2 using GraphQL.Net 2.4.0
I've created Controller to handle GraphQL Queries:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class GraphQLController : Controller
{
private readonly ISchema _schema;
private readonly IDocumentExecuter _executer;
private readonly ILogger _logger;
IEnumerable<IValidationRule> _validationRules;
private readonly DataLoaderDocumentListener _dataLoaderDocumentListener;
//private readonly IDocumentWriter _writer;
//private readonly IHttpContextAccessor _accessor;
public GraphQLController(
ILogger<GraphQLController> logger,
IEnumerable<IValidationRule> validationRules,
IDocumentExecuter executer,
DataLoaderDocumentListener dataLoaderDocumentListener,
//IDocumentWriter writer,
ISchema schema)
{
_executer = executer;
_schema = schema;
_logger = logger;
_validationRules = validationRules;
_dataLoaderDocumentListener = dataLoaderDocumentListener;
//_writer = writer;
}
[Route("graphql")]
[HttpPost]
public async Task<IActionResult> PostAsync([FromBody]GraphQLQuery query)
{
if (!ModelState.IsValid || query == null)
{
return Json("Проблем в формата на изпратената на заявка!");
}
var inputs = query.Variables.ToInputs();
var result = await _executer.ExecuteAsync(o =>
{
o.Schema = _schema;
o.Query = query.Query;
o.OperationName = query.OperationName;
o.Inputs = inputs;
o.ExposeExceptions = true;
o.EnableMetrics = true;
o.ComplexityConfiguration = new GraphQL.Validation.Complexity.ComplexityConfiguration { MaxDepth = 15 };
//o.FieldMiddleware.Use<InstrumentFieldsMiddleware>();
o.UserContext = new GraphQLUserContext
{
// this is the User on your controller
// which is populated from your jwt
User = User
};
o.ValidationRules = DocumentValidator.CoreRules().Concat(_validationRules).ToList();
o.Listeners.Add(_dataLoaderDocumentListener);
}).ConfigureAwait(false);
if (result.Errors?.Count > 0)
{
_logger.LogWarning($"Errors: {JsonConvert.SerializeObject(result.Errors)}");
return BadRequest(result);
}
return Ok(result);
}
}
The problem arises when i want to validate some of the Fields in my InputObjectGraphType , I've implemented IValidationRule interface.
Prior this im adding Metadata to the field i want to validate so i can easily find it. I;m getting the fieldType but cant fetch the Value to Validate it.
This is the Implementation of the IValidationRule i Use:
public class PhoneNumberValidationRule : IValidationRule
{
public INodeVisitor Validate(ValidationContext context)
{
return new EnterLeaveListener(_ =>
{
_.Match<Argument>(argAst =>
{
var inputType = context.TypeInfo.GetInputType().GetNamedType() as InputObjectGraphType;
var argDef = context.TypeInfo.GetArgument();
if (argDef == null) return;
var type = argDef.ResolvedType;
if (type.IsInputType())
{
var fields = ((type as NonNullGraphType)?.ResolvedType as IComplexGraphType)?.Fields;
if (fields != null)
{
foreach (var field in fields)
{
if (field.ResolvedType is NonNullGraphType)
{
if ((field.ResolvedType as NonNullGraphType).ResolvedType is IComplexGraphType)
{
fields = fields.Union(((field.ResolvedType as NonNullGraphType).ResolvedType as IComplexGraphType)?.Fields);
}
}
if (field.ResolvedType is IComplexGraphType)
{
fields = fields.Union((field.ResolvedType as IComplexGraphType)?.Fields);
}
}
//let's look for fields that have a specific metadata
foreach (var fieldType in fields.Where(f => f.HasMetadata(nameof(EmailAddressValidationRule))))
{
//now it's time to get the value
context.Inputs.GetValue(argAst.Name, fieldType.Name);
if (value != null)
{
if (!value.IsValidPhoneNumber())
{
context.ReportError(new ValidationError(context.OriginalQuery
, "Invalid Phone Number"
, "The supplied phone number is not valid."
, argAst
));
}
}
}
}
}
});
});
}
}
But here the context.Inputs property is always null.
In the controller this line var inputs = query.Variables.ToInputs();
also produces null. Is this query Variable field and Document Executer's Input Field anything to do with this ?
You probably figured it out by now, but for anyone finding this question in the future:
context.Inputs is only used with variables.
As far as I could understand argAst.GetValue could be either VariableReference or ObjectValue depending on whether the query was using a variable or not.
In case variables were not used context.Inputs.GetValue will return null.

Xunit test not working for mongodb service

I want to make a small XUnit test but it is not working. (AddTest is working but GetAllRestaurantsCountShouldReturnThree not working. )
I am new to unit testing and I don't know about Moq and how to use it.
How can I mock my IMongoService and get restaurant count?
MongoService.cs
public class MongoService : IMongoService
{
private readonly IMongoDatabase _mongoDatabase;
private readonly IMongoClient _mongoClient;
public MongoService()
{
_mongoClient = new MongoClient("mongodb://localhost:27017");
_mongoDatabase = _mongoClient.GetDatabase("Restaurant");
}
public List<RestaurantDto> GetAllRestaurants()
{
var collection = _mongoDatabase.GetCollection<RestaurantDto>("Restaurant");
return collection.Find(_ => true).ToList();
}
}
MongoServiceTest.cs
public class ReviewServiceTests
{
private List<RestaurantDto> _allRestaurants = new List<RestaurantDto>()
{
new RestaurantDto() {Name="xxx", ZipCode = "111" },
new RestaurantDto() {Name="yyy", ZipCode = "222" },
new RestaurantDto() {Name="zzz", ZipCode = "333" },
};
[Fact] //Not Working
public void GetAllRestaurantsCountShouldReturnThree()
{
var _mongoService = new Mock<IMongoService>();
_mongoService.Setup(x => x.GetAll()).Returns(_allRestaurants );
var count = _mongoService.GetAll(); //GetAll() not seeing
Assert.Equal(count, 3);
}
[Fact] //Working
public void AddTest()
{
Assert.Equal(10, Add(8, 2));
}
int Add(int a, int b)
{
return a + b;
}
}
You are using Moq incorrectly
[Fact]
public void GetAllRestaurantsCountShouldReturnThree() {
var mock = new Mock<IMongoService>();
mock.Setup(x => x.GetAllRestaurants()).Returns(_allRestaurants);
IMongoService mongoService = mock.Object;
var items = mongoService.GetAllRestaurants(); //Should call mocked service;
var count = items.Count;
Assert.Equal(count, 3);
}
Read up on the how to use Moq in their Quickstart

500 Internal Server Error for Store Method

I am gettin an null reference exception inside the Controller on the line:
return await Store.GetSearchDTO();
The error in the console reads:
POST http://localhost:55471/api/GetSearchDTO 500 (Internal Server Error)
Error: Resolving failed with a reason [object Object], but no resolveFailed provided for segment Search
Any insight on why this may be happening would be great.
Controller
namespace Api.Controllers
{
[Authorize]
[RoutePrefix("api/Search")]
public class Controller : ApiController
{
private Store _store;
public Store Store
{
get
{
return _store ?? Request.GetOwinContext().Get<Store>();
}
private set
{
_store = value;
}
}
public Controller()
{
}
public Controller(Store store)
{
Store = store;
}
[HttpPost]
[Route("GetSearchDTO")]
public async Task<SearchDTO> GetSearchDTO()
{
return await Store.GetSearchDTO();
}
}
}
Store
public async Task<SearchDTO> GetSearchDTO()
{
var toReturn = new SearchDTO();
var assessment = await Db.Definitions.Where(x => x.IsActive == true).ToListAsync();
var Types = await Db.Types.ToListAsync();
int i = 0;
int j = 0;
foreach(var assess in assessment)
{
var courseName = await Db.Courses.Where(x => x.Id == assess.CourseId).FirstOrDefaultAsync();
toReturn.CourseIds[i] = courseName.Id;
toReturn.CourseNames[i] = courseName.Name;
toReturn.Names[i] = assess.Name;
i++;
}
foreach(var type in Types)
{
toReturn.TypeIds[j] = type.Id;
toReturn.Types[j] = type.Name;
}
toReturn.SectionFlag = true;
return toReturn;
}
}
}

Moq calls original internal method in spite of Setup

Hello,
I'm having trouble getting my class to call a Moq'd method. My situation is thus:
public class ResetPasswordsTask : IRefreshTimeTask
{
public long ExecutionId => 2100;
public bool Enabled => true;
public Dictionary<string, object> Params { get; set; }
public KeyValuePair<string, Type>[] RequiredParams => new[]
{
new KeyValuePair<string, Type>("targetConfigs", typeof(InMemoryConfiguration))
};
public ILogger Logger { get; set; }
internal IConfiguration SandboxConfig;
internal IPartnerService PartnerService;
internal ISalesForceBulkDataInserter DataInserter;
public void Execute()
{
SandboxConfig = (IConfiguration)Params["targetConfigs"];
PartnerService = Login.SalesforceApiLogin(true, SandboxConfig);
DataInserter = new SalesForceBulkDataInserter();
//InitialiseImapClient();
// Retrieve users
var users = TestAutomation.Salesforce.Pages.Base.User.GetUsers(PartnerService, SandboxConfig.Refresh_Usernames);
// Upsert emails
var emailUpsertResults = UpsertEmails(users, SandboxConfig.IMAP_Email);
// Hit mailbox and visit confirmation links
var activatedUsers = emailUpsertResults.Values.Where(r => r.Status == EmailResetStatusEnum.Success).Select(r => r.User).ToList();
var confirmationLinkResults = ConfirmEmailChanges(activatedUsers);
// Upsert passwords
// All the users, except those for whom email update failed
var passwordUpdateUsers = users.Except(confirmationLinkResults.Values.Where(r => !r.Success).Select(r => r.User)).ToList();
var passwordUpsertResults = UpsertPasswords(passwordUpdateUsers);
// Hit mailbox for new tokens
var completeResetResults = RetrieveTokens(passwordUpsertResults.Values.Where(r => r.Success));
var output = string.Join("\n", completeResetResults.Values.Where(c => c.Success).Select(result => string.Join("\t", new List<string> { result.User.Username, result.Password, result.SecurityToken })));
//Logger.Info(output);
Logger.Info(System.IO.Path.GetDirectoryName("."));
Logger.Info(System.Reflection.Assembly.GetExecutingAssembly().Location);
OpenSslEncryption.EncryptToFile(SandboxConfig.EncryptionPassword, "passwords.enc", output);
}
// Results are keyed by username
internal IDictionary<string, EmailResetResult> UpsertEmails(ICollection<User> users, string newEmail)
{
var results = users.ToDictionary(u => u.Username, u => new EmailResetResult(u));
Logger.Info($"Updating emails for {users.Count} users");
foreach (var user in users)
{
Logger.Info($"updating email for {user.Username} users");
var result = results[user.Username];
// Prevent upserting the profile
if (user.ProfileId != null)
{
Logger.Info("Preventing profile upsert");
user.ProfileId.SalesForceId = user.ProfileId.OriginId;
}
// If the user has had their email set to what we're setting now, they can be skipped
if (user.Email.Equals(newEmail, StringComparison.InvariantCultureIgnoreCase) && user.IsActive)
{
Logger.Info($"User {user.Username} has their email set to {newEmail}; skipping");
result.Status = EmailResetStatusEnum.Skipped;
continue;
}
// Otherwise, set the email and carry on
user.Email = newEmail;
user.IsActive = true;
// dataInserter.Upsert won't produce errors individually, and instead only log faulty upserts
try
{
DataInserter.Upsert(new List<User> { user });
Logger.Info($"Activated user {user.Username}");
result.Status = EmailResetStatusEnum.Success;
}
catch (Exception e)
{
var error = $"Failed to update the email for user {user.Username} to {newEmail}. Error details: {e}";
Logger.Error(TestAutomation.Framework.Core.Logger.Logger.FormatJson(error));
result.ErrorMessage = e.ToString();
result.Status = EmailResetStatusEnum.Failure;
}
}
return results;
}
internal IDictionary<string, Result> ConfirmEmailChanges(ICollection<User> users)
{
var results = users.ToDictionary(u => u.Username, u => new Result(u));
// Ran as a task to have a timeout
Task.Run(() => ConfirmEmailChangesTask(results, users)).Wait(TimeSpan.FromMinutes(users.Count * 5));
return results;
}
internal void ConfirmEmailChangesTask(IDictionary<string, Result> results, ICollection<User> users)
{
var remainingUsers = new HashSet<User>(users);
while (true)
{
// Wait a bit; either for the emails to come in, or to give the webserver breathing room
Thread.Sleep(new TimeSpan(0, 0, 15));
Logger.Info($"Opening mailbox for {SandboxConfig.IMAP_Email}");
using (var imapClient = CreateImapClient())
{
var messages = imapClient.SearchMessages(SearchQuery.NotSeen.And(SearchQuery.DeliveredAfter(DateTime.Now.AddHours(-1))));
Logger.Info($"Found {messages.Count} messages");
var remainingUsersCopy = new HashSet<User>(remainingUsers);
Logger.Info($"Attempting to confirm emails for {remainingUsers.Count} users");
foreach (var user in remainingUsersCopy)
{
Logger.Info("Attempting to confirm email change for " + user.Username);
foreach (var message in messages.Where(m => m.MimeMessage.TextBody.Contains(user.Username) && m.MimeMessage.Subject.Contains("Sandbox: Finish changing your Salesforce")))
{
Logger.Info("Message found");
var confirmLink = GetEmailConfirmationLink(message);
if (confirmLink == null) continue;
// Visit the URL
var request = WebRequest.Create(confirmLink);
request.Timeout = (int)TimeSpan.FromSeconds(20).TotalMilliseconds;
var result = results[user.Username];
try
{
using (var response = (HttpWebResponse)request.GetResponse())
{
var statusCode = response.StatusCode;
if (statusCode != HttpStatusCode.OK)
{
var error = $"Failed to load the email change confirmation link: {confirmLink}. HTTP Response: ({statusCode})";
Logger.Error(TestAutomation.Framework.Core.Logger.Logger.FormatJson(error));
result.Success = false;
result.ErrorMessage = error;
}
}
}
catch (WebException e)
{
Logger.Error($"Request failed: {e.Message}\nWill retry later");
continue;
}
result.Success = true;
remainingUsers.Remove(user);
imapClient.MarkAsRead(message);
//Break down mailbox checks
Thread.Sleep(new TimeSpan(0, 0, 1));
}
}
}
if (!remainingUsers.Any())
break;
}
}
#region MailboxInteraction
internal static string GetEmailConfirmationLink(Message message)
{
// Extract confirmation URL
var confirmLinkMatch = Regex.Match(message.MimeMessage.TextBody, #"([a-z]+:\/\/.*\.salesforce\.com\/\S*)");
return !confirmLinkMatch.Success ? null : confirmLinkMatch.Groups[1].Value;
}
internal static string GetSecurityToken(Message message)
{
var tokenMatch = Regex.Match(message.MimeMessage.TextBody, #"Security token \(case-sensitive\): (?<token>\w+)");
return !tokenMatch.Success ? null : tokenMatch.Groups[1].Value;
}
internal virtual IMailClient CreateImapClient()
{
return new IMAPClient(SandboxConfig.IMAP_Username, SandboxConfig.IMAP_Password, SandboxConfig.IMAP_URL);
}
#endregion
}
Test class:
[TestFixture]
public class WhenResettingUserPasswords
{
private const string ConfirmationLink = "test://testdomain.salesforce.com/test/";
[OneTimeSetUp]
public void WebRequestSetup()
{
WebRequest.RegisterPrefix("test", TestableWebRequestCreateFactory.GetFactory());
var uri = new Uri("test://testdomain.salesforce.com/test/");
var expectedRequest = new TestableWebRequest(uri);
expectedRequest.EnqueueResponse(HttpStatusCode.OK, "Success", "Even more success!", false);
TestableWebRequestCreateFactory.GetFactory().AddRequest(expectedRequest);
}
private static SetupBag Setup()
{
var bag = new SetupBag
{
Logger = new InMemoryLogger(),
EmailConfirmationLink = ConfirmationLink,
SecurityToken = "TheSecurityToken",
Environment = "EnvName",
EnvironmentUrl = "http://aaa.bbb.ccc/",
User = new User
{
IsActive = false,
Username = "joe.bloggs#company.com",
Email = "joe.bloggs=company.com#example.com",
OriginId = "ABCDEFGHIJKLMNO"
}
};
var task = new Mock<Tasks.ResetPasswordsTask>(MockBehavior.Strict) { CallBase = true };
task.Object.Logger = bag.Logger;
var confirmMessage = new Message
{
UID = new UniqueId(0),
MimeMessage = new MimeMessage
{
Subject = "Sandbox: Finish changing your Salesforce",
Body = new TextPart("plain") { Text = "Confirm email change for joe.bloggs#company.com: " + bag.EmailConfirmationLink }
}
};
var tokenMessage = new Message
{
UID = new UniqueId(1),
MimeMessage = new MimeMessage
{
Subject = "Sandbox: Your new Salesforce security token",
Body = new TextPart("plain") { Text = "New security token for joe.bloggs#company.com: " + bag.SecurityToken }
}
};
var mailClientMock = new Mock<IMailClient>(MockBehavior.Strict);
mailClientMock.Setup(m => m.SearchMessages(It.IsAny<SearchQuery>())).Returns(new List<Message> { confirmMessage, tokenMessage });
task.Setup(t => t.CreateImapClient()).Returns(() => mailClientMock.Object);
var dataInserterMock = new Mock<ISalesForceBulkDataInserter>();
dataInserterMock.Setup(m => m.Upsert(It.IsAny<List<User>>(), false));
var config = new InMemoryConfiguration
{
IMAP_Email = "test.email#company.com"
};
task.Object.SandboxConfig = config;
bag.Task = task;
return bag;
}
[Test]
public void UpsertEmailsTest()
{
var bag = Setup();
var task = bag.Task;
var output = task.Object.ConfirmEmailChanges(new[] { bag.User });
Assert.IsTrue(output.ContainsKey(bag.User.Username));
Assert.IsTrue(output[bag.User.Username].Success);
Assert.IsEmpty(output[bag.User.Username].ErrorMessage);
Assert.AreEqual(task.Object.SandboxConfig.IMAP_Email, output[bag.User.Username].User.Username);
}
}
Now, the result is that calling task.Object.ConfirmEmailChanges() raises an exception about arguments to new IMAPClient() being null, which should not have been called at all. I could not find anything obviously wrong, but that might just be because I'm not familiar enough with Moq or the way my codebase does testing.
Yes, I know I'm mocking a class I want to test. I know it's a bad idea, and I'm aiming for a redesign. I cannot inject IMailClient as a dependency because it needs to be instantiated anew frequently. I have also seen this sort of technique used in our codebase, and it seemed to work.
If you are mocking a concrete class and not an interface then you must make the method you want to mock virtual.
Edit: in your new code the method is also marked as internal. See this question.
You need to declare your method as virtual or Moq is unable to override it. C# is not like Java - only members that are specifically marked are able to be overridden.
EDIT
The code you've posted should work - here is a Linqpad MVCE I generated based on your code, demonstrating that it works correctly. Therefore, you will need to post your actual code, as it has a bug in it.
void Main()
{
// Arrange
var foo = new Mock<Foo> { CallBase = true };
var bar = new Mock<IBar>();
bar.Setup(b => b.Value).Returns(2);
// setup an IBar mock
foo.Setup(f => f.CreateBar()).Returns(bar.Object);
// Act
var results = foo.Object.DoStuff();
results.Dump(); // Prints "2"
}
public class Foo
{
public int DoStuff()
{
var bar = CreateBar();
return bar.Value;
}
public virtual IBar CreateBar()
{
return new RealBar();
}
}
public interface IBar
{
int Value { get;}
}
public class RealBar : IBar
{
public int Value
{
get { return 1; }
}
}

Categories