2
\$\begingroup\$

Class To Test

Some methods hidden for clarity

namespace RiPS.Infrastructure.UA
{
    public class UAWorkflowManager
    {
        private readonly IContextIOAPI _api;
        private readonly IUAjoinedRepo _dbJoinedRepo;
        private readonly IRemindersSystem _remindersSystem;


        public UAWorkflowManager(IContextIOAPI contextIOAPI, IUAjoinedRepo uAjoinedRepo, IRemindersSystem remindersSystem)
        {

            _api = contextIOAPI;
            _dbJoinedRepo = uAjoinedRepo;
            _remindersSystem = remindersSystem;
        }


        public bool Assign(MongoUAJoinedWorkflow wf,string username, IUAWorkflowHub hub)
        {
                if (String.IsNullOrEmpty(wf.UA))
                {
                    hub.Error("Assignment Error", "Must have UA to assign");
                    return false;
                }
                if (wf.TaskReqs.Count < 1)
                {
                    hub.Error("Assignment Error", "Must have tasks");
                    return false;
                }
                foreach (var req in wf.TaskReqs)
                {
                    if (String.IsNullOrEmpty(req.Note))
                    {
                        hub.Error("Assignment Error", "All Tasks must have notes");
                        return false;
                    }

                    if (String.IsNullOrEmpty(req.Title))
                    {
                        hub.Error("Assignment Error", "All Tasks must have titles. Must be an error  ");
                        return false;
                    }
                }

                wf.Assigned = true;
                wf.AssignedBy = username;
                wf.AssignedDate = DateTime.UtcNow;

                var msg = GenerateMessage(wf);
                msg.Body = RenderViewToString(wf);
                var str = CreateStream(wf, hub);
                var fileName = str.Item1;
                var stream = str.Item2;
                AttachMessage(msg, stream, fileName);
                hub.Info("Assigning", "Sending Email");
                RetryUtility.RetryAction(() => SendMessage(msg));
                stream.Position = 0;
                hub.Info("Task", "adding");
                foreach (var taskReq in wf.TaskReqs)
                {
                   var r = _remindersSystem.AddReminder(new MongoReminder(wf.clientId, wf.policyId, wf.UA, "UAWF", taskReq.DueDate, taskReq.Title), stream, fileName);
                   wf.ReminderGuids.Add(r.Id);
                       stream.Position = 0;
                }
                _dbJoinedRepo.Save(wf);
                hub.Info("Task", "added");
                hub.Info("Assigning", "Moving Email To Assigned Folder");
                RetryUtility.RetryAction(() => MoveToFolder(wf.MessageId, "Assigned"));
                hub.Info("Assigning", "Saving Assignment to Database");
                wf.Error = "";

                hub.Update(wf);


                hub.Success("Assigned", wf.Subject);
                return true;
        }
    }
}   

Test Class (nUnit & Moq)

namespace RiPSTests
{
    [TestFixture]
    public class UAWF
    {
        private Mock<IContextIOAPI> mock;
        private Mock<IUAjoinedRepo> repoMock;
        private Mock<IUAWorkflowHub> hubMock;
        private Mock<IRemindersSystem> reminderMock;
        private const string ConstSampleUA = "FAKEUA";
        private const string ConstSampleNote = "FAKENote";
        private const string ConstSampleTitle = "FAKETitle";
        [SetUp]
        public void setup()
        {
            mock = new Mock<IContextIOAPI>();
            repoMock = new Mock<IUAjoinedRepo>();
            hubMock = new Mock<IUAWorkflowHub>();
            reminderMock = new Mock<IRemindersSystem>();
        }
        [Test]
        public void CanSetup()
        {

            var man = new UAWorkflowManager(mock.Object, repoMock.Object, reminderMock.Object);
            Assert.NotNull(man);
        }

        [Test]
        public void FailMustHaveUA()
        {
            //Arrange 
            var man = new UAWorkflowManager(mock.Object, repoMock.Object, reminderMock.Object);
            var wf = new MongoUAJoinedWorkflow();

            //Act
            var res = man.Assign(wf, ConstSampleUA, hubMock.Object);

            //Assert
            Assert.IsFalse(res);
            hubMock.Verify(z => z.Error("Assignment Error", "Must have UA to assign"), Times.Exactly(1));
        }

        [Test]
        public void FailMustHaveTasks()
        {
            //Arrange 
            var man = new UAWorkflowManager(mock.Object, repoMock.Object, reminderMock.Object);
            var wf = new MongoUAJoinedWorkflow()
            {
                UA = ConstSampleUA
            };

            //Act
            var res = man.Assign(wf, ConstSampleUA, hubMock.Object);

            //Assert
            Assert.IsFalse(res);
            hubMock.Verify(z => z.Error("Assignment Error", "Must have tasks"), Times.Exactly(1));
        }


        [Test]
        public void FailTasksMustHaveNote()
        {
            //Arrange 
            var man = new UAWorkflowManager(mock.Object, repoMock.Object, reminderMock.Objects);
            var wf = new MongoUAJoinedWorkflow()
            {
                UA = ConstSampleUA,
                TaskReqs =  new List<TaskReq>()
                {
                    new TaskReq()
                    {
                        DueDate = DateTime.Today,
                        Title = ConstSampleTitle

                    }
                }

            };

            //Act
            var res = man.Assign(wf, ConstSampleUA, hubMock.Object);

            //Assert
            Assert.IsFalse(res);
            hubMock.Verify(z => z.Error("Assignment Error", "All Tasks must have notes"), Times.Exactly(1));
        }

    }
}

Test feel awfully verbose and brittle. Writing Tests Second (Need to do some overhaul and was unable to test the first time because i am not a strong tester)

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Your first test, CanSetup, will never fail; a .NET constructor can never return null.

All of your tests have the same basic setup: create a "manager", create a "workflow", invoke the Assign() method w/ the same arguments. All that changes is how the WF is constructed. Consider:

  1. Make the manager part of the fixture (not a local variable) and initialize it during setup. It seems to be initialized the same in all cases.

  2. Create a private helper function that encapsulates the pattern you follow, and just expose the things that vary (like the WF, or some properties of the WF) as arguments. When each test can essentially be expressed as a form of template, this is a decent way of reducing duplication.

  3. Personal preference, but I like underscores in my test names; makes it easier to read a long list of them and to spot differences. I also like fairly verbose names that describe the business case for the test: Cannot_assign_a_UA_without_tasks rather than FailMustHaveTasks. (I have no idea if I inferred the purpose of the test correctly, which is exactly the point I'm making; FailMustHaveTasks makes no sense to me at all)

  4. Re: tests being brittle. If you're asserting against an error message, avoid doing an Equals assert. Someone could fix a grammatical mistake and break the test. Instead, just look for one or two identifying words. I'm not sure what the syntax is w/ Moq, but in RhinoMocks it's pretty easy to say "assert this method was called with a string argument containing the word 'Foo'".

  5. Why else are your tests brittle? Sometimes brittle tests are caused by people asserting the wrong thing; do you really care that a specific method was called? Or do you just care that some object wasn't modified? Tests that assert against specific interactions can be more brittle than tests that assert against state.

\$\endgroup\$
1
  • \$\begingroup\$ To be more precise: The test CanSetup can fail but the Assert never will (the ctor could throw an exception which will make the test fail but if there is no exception then the Assert will never fail). \$\endgroup\$
    – ChrisWue
    Jan 24, 2014 at 22:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.