O teste de unidade é útil apenas se estiver testando a implementação correta da referida função, não o uso correto, porque um teste de unidade não pode dizer que uma função não será chamada de outro thread no restante da sua base de código, da mesma forma que pode ' diga que funções não serão chamadas com parâmetros que violam suas condições prévias (e tecnicamente o que você está tentando testar é basicamente uma violação de uma pré-condição em uso, algo que você não pode testar efetivamente porque o teste pode ' para restringir como outros lugares na base de código usam essas funções; você pode testar se violações de pré-condições resultam em erros / exceções apropriados).
Este é um caso de afirmação para mim na implementação das funções relevantes, como algumas pessoas apontaram, ou ainda mais sofisticado é garantir que as funções sejam seguras contra threads (embora isso nem sempre seja prático quando se trabalha com algumas APIs).
Também apenas uma nota lateral, mas "thread principal"! = "Thread da interface do usuário" em todos os casos. Muitas APIs da GUI não são seguras para threads (e fazer um kit GUI seguro para threads é muito difícil), mas isso não significa que você precise invocá-las a partir do mesmo thread que aquele que tem o ponto de entrada para o seu aplicativo. Isso pode ser útil mesmo na implementação de suas asserções dentro das funções relevantes da interface do usuário para distinguir "thread da interface do usuário" do "thread principal", como capturar o ID do thread atual quando uma janela é criada para comparar, em vez do ponto de entrada principal do aplicativo (que pelo menos reduz a quantidade de suposições / restrições de uso que a implementação está aplicando apenas ao que é realmente relevante).
A segurança do encadeamento foi, na verdade, o ponto de partida de "pegadinha" em uma antiga equipe minha e, no nosso caso particular, eu a teria rotulado como a "micro-otimização" mais contraproducente de todos, de um tipo que incorreu em mais custos de manutenção do que nunca montagem manuscrita. Tivemos uma cobertura de código bastante abrangente em nossos testes de unidade, juntamente com testes de integração bastante sofisticados, apenas para encontrar conflitos e condições de corrida no software que escapou aos nossos testes. E isso aconteceu porque os desenvolvedores aleatoriamente multithreaded code sem estarem cientes de todos os efeitos colaterais que poderiam ocorrer na cadeia de chamadas de funções que resultariam de seus próprios, com uma idéia bastante ingênua de que eles poderiam corrigir esses erros em retrospectiva apenas jogando trava em torno de esquerda e direita,
Eu estava inclinado na direção oposta, como um tipo da velha escola que desconfiava de multithreading, era um verdadeiro retardatário em adotá-lo, e pensei que a correção supera o desempenho a ponto de raramente se aproveitar de todos esses núcleos que temos agora, até descobrir coisas. como funções puras, designs imutáveis e estruturas de dados persistentes que finalmente me permitiram utilizar totalmente esse hardware sem me preocupar com as condições de corrida e os impasses. Devo admitir que até 2010, mais ou menos, eu odiava multithreading com paixão, exceto por alguns loops paralelos aqui e ali em áreas que são triviais para raciocinar sobre segurança de threads e favorecia um código muito mais seqüencial para o design de produtos, dada a minha dor com multithreading em equipes anteriores.
Para mim, esse modo de multithreading primeiro e corrigir erros mais tarde é uma estratégia terrível para multithreading a ponto de quase me fazer odiar o multithreading inicialmente; você assegura que seus projetos sejam seguros para encadeamento e que suas implementações usem apenas funções com garantias semelhantes (por exemplo: funções puras) ou evite multithreading. Isso pode parecer um pouco dogmático, mas é melhor do que descobrir (ou pior, não descobrir) problemas difíceis de reproduzir em retrospectiva que escapam aos testes. Não faz sentido otimizar um motor de foguete se isso resultar em uma explosão inesperada inesperada do nada no meio do caminho ao longo de sua jornada para o espaço.
Se você inevitavelmente precisar trabalhar com código que não é seguro para threads, não vejo isso como um problema a ser resolvido tanto com testes de unidade / integração. O ideal seria restringir o acesso . Se o seu código da GUI estiver dissociado da lógica de negócios, você poderá aplicar um design que restrinja o acesso a essas chamadas por qualquer coisa que não seja o encadeamento / objeto que o cria *. Esse modo distante ideal para mim é tornar impossível para outros threads chamar essas funções do que tentar garantir que não.
- Sim, percebo que sempre há maneiras de contornar as restrições de design que você impõe normalmente, onde o compilador não pode protegê-lo. Estou apenas falando praticamente; se você pode abstrair o objeto "Thread da GUI" ou qualquer outra coisa, pode ser o único que entregou um parâmetro aos objetos / funções da GUI e poderá restringir esse objeto de ter acesso a outros threads. É claro que pode ser capaz de contornar e cavar fundo e contornar esses aros para passar as funções / objetos da GUI para outros segmentos a serem invocados, mas pelo menos há uma barreira lá, e você pode chamar qualquer pessoa que faça isso de "idiota" ", e não estar errado, pelo menos, por ignorar claramente e procurar brechas para o que o design obviamente estava tentando restringir. :-D Isso '