📜 ⬆️ ⬇️

Replacing Algorithm Testing with Testing Introduced Effects

As expected, rule 8 that we are not testing the algorithm of methods in the article " Rules for implementing TDD in the old project " caused the most questions "how" and "why". At the time of the last article I thought it was obvious, so I did not stop at this point in more detail. But since There were a lot of questions, I want to describe my vision. Therefore, under the cut there will be a small code sample and two examples of how it could be tested.

In order not to force you to run to the previous article, I will give the rule “Do not test the algorithm of methods” again, as it sounded there:
The name of the rule was unsuccessfully chosen here, but I haven’t yet come up with a better one. Among the “Mokists” (these are those who mock up in tests) there are those who check the number of calls to certain methods, verify the call itself, etc. In other words, it checks the internal workings of methods. This is as bad as private testing. The difference is only in the level of application of such verification. This approach again gives a lot of fragile tests, because of what TDD is not perceived by some as normal.

There is the following handler code:

public class SomeEventHandler { public SomeEventHandler(IDatabaseCommands dbCommands, IEventValidator validator, IMessagingLogger messagingLogger) { // skipped } public HandlerResult Handle(EventPayload payload) { if (Validator.IsOurEvent(payload)) if (Validator.IsValid(payload)) { var evt = Mapper.Map<Event>(payload); try { using (var tran = new TransactionScope()) { DbCommands.SaveEvt(evt); MessagingLogger.Received(payload); tran.Complete(); } } catch (Exception ex) { return MessageHandlerResult.Fatal; } } else { var error = Validator.GetErrors(); MessagingLogger.InvalidEvent(payload, error); return MessageHandlerResult.Fatal; } return MessageHandlerResult.Success; } } 

It is necessary to test the operation of the Handle () method. The question is to make sure that the DbCommands and MessagingLogger methods are called.

Mokista approach


He would pass the appropriate interfaces to the mochi class constructor, and after that he would check whether the corresponding methods were called SaveEvt (), Received () or InvalidEvent (). The code would look something like this:
')
 public void Should_save_valid_data_and_log_to_messaging_events() { var builder = new EventPayload { // skipped }; var validator = Mock.Of<IEventValidator>(); var dbCommands = new Mock<IDatabaseCommands>(); var messagingLogger = new Mock<IMessagingLogger>(); var handler = new SomeEventHandler(dbCommands, validator, messagingLogger); var result = handler.Handle(payload); // assertions Assert.Equal(MessageHandlerResult.Success, result); dbCommands.Verify(m => m.SaveEvt(It.IsAny<Event>(), Times.Once()) messagingLogger.Verify(m => m.Received(It.IsAny<EventPayload>(), Times.Once()) } 

Nemokist Approach


He would create fake objects and check if the event as a whole would happen, rather than a method call. In this case, the code would be approximately as follows:

 public void Should_save_valid_data_and_log_to_messaging_events() { var builder = new EventPayload { // skipped }; var validator = Mock.Of<IEventValidator>(); var dbCommands = new FakeDatabaseCommands(); var messagingLogger = new FakeMessagingLogger(); var handler = new SomeEventHandler(dbCommands, validator, messagingLogger); var result = handler.Handle(payload); // assertions Assert.Equal(MessageHandlerResult.Success, result); Assert.True(dbCommands.IsEventSaved); Assert.True(messagingLogger.IsEventRegistered); } 

And fake-objects methods would look like this:

 public void SaveEvt(Event evt) { IsEventSaved = true; } 

In this case, IsEventSaved would be declared only in the fake object.

Advantages and disadvantages


The first approach is simple and fast, but if you need to change methods, call one, instead of the other in the same situation, then the tests would need to be corrected.

The second approach leads to the creation of additional entities, and the gain is obtained only in the situation with the replacement of methods. In this case, you may not even have to change anything in Fakes or in tests. Another plus, however, more idealistic, is that the nemokist makes the test so that he does not know about the internal structure of the test method. Therefore, I personally, if time allows, do tests on fakes.

Source: https://habr.com/ru/post/315114/


All Articles