On Github a10nik / myf-testing
Артем Десятников
Самый интуитивный метод
Иногда нельзя иначе
иначе тест ТьюрингаПервого рода — посчитать плохую программу хорошей
Второго рода — посчитать хорошую программу плохой
Тестирование минимальных кусочков приложения по-отдельности.
Надо сделать их независимыми
падают только если ошибка в коде кусочка. удобно искать. обычно - только при разработке кусочка или внесении изменений в кусочек. иногда - при рефакторингахclass TodoList {
  private TodoItem[] items;
  public TodoList() {
    this.items = Db.GetTodoItems();
  }
}
void MainLogic() {
  var list = new TodoList();
  Render(list);
}
class TodoList {
  private TodoItem[] items;
  public TodoList() {
    this.items = Db.GetTodoItems();
  }
}
void Test() {
  var list = new TodoList();
  ???
}
Dependency Injection
class TodoList {
  private TodoItem[] items;
  public TodoList(TodoItem[] items) {
    this.items = items;
  }
}
void MainLogic() {
  var db = new Db("login", "pass", catalog: "items");
  var items = db.GetTodoItems();
  var list = new TodoList(items);
  Render(list);
}
void Test() {
  var list = new TodoList(new[] { new TodoItem() { Text = "Первая тудушка", Done = false } });
  var rendered = Render(list);
  Assert.That(rendered, Contains.Substring("Первая тудушка"));
}
[TestFixture]
public class TodoListTest
{
  [Test]
  public void HasNothingToDoWhenEmpty()
  {
    var list = new TodoList(new TodoItem[] {});
    Assert.That(list.HasSmthToDo, Is.False);
  }
}
describe("todo list", () => {
  it("has nothing to do when empty", () => {
    let list = new TodoList([]);
    expect(list.hasSmthToDo).to.be.false;
  });
});
Assert.AreNotEqual(5, 2 + 2);
// vs
Assert.That(2 + 2, Is.Not.EqualTo(5));
Assert.That(SomeMethod, Throws.TypeOf()
    .With.Property("Parameter").EqualTo("myParam"));
class Db {
  ...
  public TodoItem[] GetTodoItems() {
    return new DbConnection(login, password, catalog)
      .Query("select * from todos")
      .Select(row => new TodoItem
      {
        Text = row["text"],
        Done = row["done"] == "1"
      });
  }
  public void SaveTodoItems(TodoItem[] items) {
    new DbConnection(login, password, catalog)
      .Query("insert or update items " + ...));
  }
}
Dependency Inversion
class Db {
  private IDbConnection connection;
  ...
  public TodoItem[] GetTodoItems() {
    return connection
      .Query("select * from todos")
      .Select(row => new TodoItem
      {
        Text = row["text"],
        Done = row["done"] == "1"
      });
  }
  public void SaveTodoItems(TodoItem[] items) {
    connection.Query("insert or update items " + ...);
  }
}
Dependency Inversion
class TestEmptyConnection : IDbConnection {
  public RowSet Query(string query) { return new RowSet(); }
  public void Dispose() {}
}
[Test]
void AllDoneWhenListIsEmpty() {
  var db = new Db(new TestEmptyConnection());
  Assert.That(db.GetTodoItems(), Is.Empty);
}
[Test]
void DbYieldsEmptyItemListWhenRowSetIsEmpty() {
  var fakeConnection = A.Fake();
  A.CallTo(() => fakeConnection.Query("select * from todos"))
    .Returns(new RowSet());
  var db = new Db(fakeItemProvider);
  Assert.That(db.GetTodoItems(), Is.Empty);
}
new MainLogic(new Db(fakeConnection));
Затычки на каком-то уровне. Поверх - настоящая логика
class DbUtils {
  public TodoItem RowToTodoItem(Row row) { ... }
  public string ToUpdateQuery(TodoItem[] items) { ... }
}
[Test]
void RowToTodoItemConvertsSimpleRow() {
  var row = new Row();
  row.Add("done", "0");
  row.Add("text", "hello");
  var todoItem = DbUtils.RowToTodoItem(row);
  Assert.That(todoItem.Text, Is.EqualTo("hello"));
  Assert.That(todoItem.Done, Is.False);
}
Чистые функции. Декомпозиция.
Тестирование в браузере
хрупкие. и ошибки расплывчатые