2

I'm new to using Moq. I am trying to get this unit test to work but my object seems to keep returning null. I saw online that the Setup() must match the actual call. I'm obviously not getting it cause it still doesn't work; it seems to match to me. Here's my code samples.

Test method from test project:

    [TestMethod]
    public void CanPutEmailOptOut()
    {
        var mockParticipant = new PscuParticipant
            {
                ParticipantId = 1,
                DoNotSendCuRewardsEmails = false,
                DoNotSendEarnBonusPointEmail = false,
                CardNumber = "VPZS5zXFUex2SJikkXFVrnvt2/R38yomFXwkslgXNKkgAFsjvt94p1h6J/XUEc6yQ5JzmT6+W8AdxuBSbp9e0SXAN60oHuZtWhAgGHhU+GaxJfCQHitc2+VBSZ/DxwW7Bpw="
            };

        MockBootstrapper.Instance.WithRepositoryData(new[] {mockParticipant});
        var input = new EmailOptOutContract
            {
                DoNotSendCuRewardsEmails = true,
                DoNotSendEarnBonusPointEmail = true
            };

        _affinityOptOutApiClient
            .Setup(
                x =>
                x.CallAffinityOptOutStatus(It.IsAny<string>(), 
                                           It.IsAny<string>(),
                                           mockParticipant.DoNotSendEarnBonusPointEmail,
                                           mockParticipant.ParticipantId))
            .Returns<HindaHttpResponse<OptOutResponse>>(x => new HindaHttpResponse<OptOutResponse>
                {
                    StatusCode = AffinityResultCode.Success,
                    ResponseObject = new OptOutResponse { MemberId = "999999999", Status = "success" }
                });

        var response = Controller.Put(mockParticipant.ParticipantId, input);

        var contract = response.ShouldBeSuccess<SuccessContract>();
        var participant = RepositoryFactory.CreateReadOnly<PscuParticipant>().FirstOrDefault(x => x.ParticipantId == mockParticipant.ParticipantId);
        Assert.AreEqual(input.DoNotSendCuRewardsEmails, participant.DoNotSendCuRewardsEmails);
        Assert.AreEqual(input.DoNotSendEarnBonusPointEmail, participant.DoNotSendEarnBonusPointEmail);
    }

    protected override void Configure()
    {
        MockBootstrapper.Override(config => config.For<IEncryptionService>().Use<EncryptionService>());
        _affinityOptOutApiClient = new Mock<IAffinityOptOutApiClient>(MockBehavior.Strict);
        MockBootstrapper.Override(config => config.For<IAffinityOptOutApiClient>().Use(_affinityOptOutApiClient.Object));
    }

Here's the method from my controller:

    public HttpResponseMessage Put(int participantId, [FromBody]EmailOptOutContract contract)
    {
        if (contract == null)
            return Failure(ApiReturnCodes.InvalidRequestContract
                            , "Invalid Request Contract",
                            string.Format("Contract Is Null in controller method {0}", System.Reflection.MethodBase.GetCurrentMethod()),
                            HttpStatusCode.BadRequest);

        using (new UnitOfWorkScope())
        {
            var participant = GetParticipant(participantId);
            if (participant == null)
            {
                return NotFound(ApiReturnCodes.ParticipantNotFound, "Participant ID not found.");
            }

            participant.DoNotSendCuRewardsEmails = contract.DoNotSendCuRewardsEmails;
            participant.DoNotSendEarnBonusPointEmail = contract.DoNotSendEarnBonusPointEmail;

            string cardNumber = ServiceLocator.Current.GetInstance<IEncryptionService>().Decrypt(participant.CardNumber);
            cardNumber = AesEncrypt(cardNumber);

            string email = null;
            var partic = GetParticipantData(participant.ParticipantId);

            if (partic != null)
                email = partic.Email;

            HindaHttpResponse<OptOutResponse> response =
                _affinityOptOutApiClient.CallAffinityOptOutStatus(cardNumber, email, contract.DoNotSendEarnBonusPointEmail, participant.ParticipantId);

            if (response.StatusCode == AffinityResultCode.Success && response.ResponseObject.Status == "success")
                participant.AffinityMembId = response.ResponseObject.MemberId;
            else
                return BadRequest(ApiReturnCodes.AffinityInternalServerError, response.ExternalErrorMessage);

            return Ok();
        }
    }

The part that comes back null in the controller is

HindaHttpResponse<OptOutResponse> response =
                _affinityOptOutApiClient.CallAffinityOptOutStatus(cardNumber, email, contract.DoNotSendEarnBonusPointEmail, participant.ParticipantId);

The response object is null so when it is checked in the next statement for success, the exception is thrown. Does anyone know what might be wrong with my Setup/Return that's causing problems?

Thanks!!!!

cnotes
  • 247
  • 1
  • 4
  • 15

2 Answers2

1

In your controller you're changing participant.DoNotSendCuRewardsEmails to the value in the contract object, which in your setup of that is false. You setup your method to expect true for that parameter as that is the value contained in participant when setup is called. Moq gets the value of the property as is when setup is called, it doesn't lazy evaluate the objects property.

Andy
  • 8,432
  • 6
  • 38
  • 76
  • Prior to posting this, I was passing in the mockParticipant.CardNumber into the Setup() instead of It.IsAny(). In the controller, I was passing in the same corresponding variable, but it's value had changed as in the controller, it is being decrypted and encrypted using a different encryption process then passed into the method. Could this have been causing a problem as well? Does Moq just evaluate to see if the values passed in are the same? Or, do the variables have to come from the same place? For example, I passed in different variables; if values were same, would it worked? – cnotes Nov 20 '14 at 16:34
  • @cnotes Yes, Moq records the values you give at Setup and then those values must match exactly or it will return the default return type instead of the object you specified. This is helpful if you want to have different objects returned based on what values the mocked method was called with. – Andy Nov 20 '14 at 17:32
0

When you setup the mock, you have to use input

x.CallAffinityOptOutStatus(
    It.IsAny<string>(), 
    It.IsAny<string>(),
    input.DoNotSendEarnBonusPointEmail,
    mockParticipant.ParticipantId)

It needs to match the specific call you are making inside the controller.

beautifulcoder
  • 10,832
  • 3
  • 19
  • 29
  • Thanks for the response, but I have tried exactly that and I got the "Parameter count mismatch" error, which is what I seem to be getting mostly now. Any ideas on that? – cnotes Nov 19 '14 at 23:19
  • You could just hard code it to `true` for now, just to see if that works – beautifulcoder Nov 19 '14 at 23:26
  • Tried that too and it didn't work. I've found the solution to both my problems. Here's a link to the parameter count issue http://stackoverflow.com/questions/7714551/moq-unit-testing-system-reflection-targetparametercountexception-parameter. Thanks for your help. – cnotes Nov 19 '14 at 23:33