3

Scenario: we want test drive a create method, the resulting method would look like this:

public MyModel Create(MyModel model){
    var dao = TransformToDao(model);
    dao.Date = DateTime.Now;
    var result = _repository.Create(dao);
    return TranformToModel(result);
}

I think two test points for this method should be:

  1. Create should set a date to current date time
  2. Create should create(save) model

But I see some problems here. The method has an output, but functionality here is not to set the date, but rather save with the date set, so checking output doesn't look like the right think to do. For example, this method would also pass the test:

public MyModel Create(MyModel model){
    var dao = TransformToDao(model);
    var result = _repository.Create(dao);
    var model = TranformToModel(result);
    model.Date = DateTime.Now;
    return model;
}

If I want to drive create(save) functionality I need to mock repository, but then I am dictating particular implementation. Should I mock repository and just check if it was called with dao, that has set DateTime?

_repository.AssertWasCalled(c => c.Create(Arg<MyDao>.Matches(x => (int) (x.Date - DateTime.Now).TotalSeconds == 0)));

This leads to tests like this one:

public MyModel Update(MyModel model){
    return _repository.Update(model);
}

[Test]
public void UpdateShouldCallRepositoryUpdate(){
   var model = new MyModel();
   _repository.Expect(x => x.Update(model)).Return(model);

   _service.Update();

   _repository.AssertWasCalled(x => x.Update(model));
}

This kind of tests looks very redundant to me, but perhaps it is the way to do it? Or perhaps there is some kind of problem in the architecture or tests?

  • 3
    As an aside you should also abstract the date time retrieval as a dependency which would give you more flexibility when testing the date. – Nkosi Jan 31 '17 at 11:05
  • Your Create method have a side effect - saving created object to the database. I think it will be better to test only creation of new object with "expected" DateTtime and then in another method save it to the database – Fabio Feb 2 '17 at 13:14
  • @Fabio But should I make Create method public? It doesn't make sense in application context to create something without saving it, but if it is not public and just part of Save method how would I test it? – Perpetuum Feb 3 '17 at 8:21
  • Without example of how your Create method used it difficult to decide how ti can be "refactored". – Fabio Feb 3 '17 at 8:42
  • @Fabio Examples I gave are intentionally abstract because my question is about common principal not about a particular case. I can see how separating the Create method would solve some problems and let me test only the output, but let's assume that Create is always followed by Save is it correct to separate those two methods just for "tests sake"? – Perpetuum Feb 3 '17 at 8:55
2

That's how it's done. It makes sure that if someone were to remove that line from your code in the future, the test would fail. In fact, if you are using TDD, then you would need to write that test before writing implementation. I just would make sure that your _repository is an Interface, instead of an actual implementation, and as Nkosi mentioned, you may want to inject an ITimeService (with GetCurrentTime() member) as well, instead of depending on DateTime.Now directly.

  • I know how TDD works, the problem is that to "drive" my service implementation I need to create tests that mock repository methods and then asserts that those methods were called (is it not redundant?). Also, I am not testing outputs, but instead dictating the particular implementation inside the method. – Perpetuum Feb 3 '17 at 8:41
  • 2
    @Perpetuum But you are testing the outputs. In this case, the output is how your service interacts with the repository. Google 'mockist TDD' or 'London school' TDD. – Eternal21 Feb 3 '17 at 12:26
  • 1
    @Perpetuum you're not wrong. You really just want to test outputs, but this is part of a thin layer where the only way to assert correct behavior is to spy on the implementation or to actually save to a database and then verify the expected record is there. I'd recommend staying away from the latter. – RubberDuck Mar 5 '17 at 12:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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